Base implementation

This commit is contained in:
Andrea Lamparelli 2022-12-23 19:03:23 +01:00
parent 05d156a5b0
commit 74703c48f3
53 changed files with 34684 additions and 392 deletions

View file

@ -43,5 +43,9 @@
"error", "error",
"as-needed" "as-needed"
] ]
},
"env": {
"node": true,
"es2020": true
} }
} }

9
.github/dependabot.yml vendored Normal file
View file

@ -0,0 +1,9 @@
version: 2
updates:
# Enable version updates for npm
- package-ecosystem: 'npm'
# Look for `package.json` and `lock` files in the `root` directory
directory: '/'
# Check the npm registry for updates every day (weekdays)
schedule:
interval: 'daily'

20
.github/pull_request_template.md vendored Normal file
View file

@ -0,0 +1,20 @@
**Thank you for submitting this pull request**
fix _(please add the issue ID if it exists)_
### Referenced pull requests
<!-- Add URLs of all referenced pull requests if they exist. This is only required when making
changes that span multiple kiegroup repositories and depend on each other. -->
<!-- Example:
- https://github.com/kiegroup/droolsjbpm-build-bootstrap/pull/1234
- https://github.com/kiegroup/drools/pull/3000
- https://github.com/kiegroup/optaplanner/pull/899
- etc.
-->
### Checklist
- [ ] Documentation updated if applicable.
- [ ] Changelog updated if applicable.
> **Note:** `dist/cli/index.js` and `dist/gha/index.js` are automatically generated by git hooks and gh workflows.

27
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,27 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: "Master CI and Pre Release"
on:
push:
branches:
- master
jobs:
build-and-test:
strategy:
matrix:
node-version: [16]
os: [ubuntu-latest]
fail-fast: true
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Setup Node ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test

24
.github/workflows/pull-request.yml vendored Normal file
View file

@ -0,0 +1,24 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: "Pull Request Checks"
on: pull_request
jobs:
build:
strategy:
matrix:
node-version: [16]
os: [ubuntu-latest]
fail-fast: false
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Setup Node ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test

44
.github/workflows/release.yml vendored Normal file
View file

@ -0,0 +1,44 @@
# This workflow create a new tag, a release and then publish it to NPM.
name: "Create Release"
on:
workflow_dispatch:
inputs:
version:
description: 'version to bump [major, minor, patch, premajor, preminor, prepatch, prerelease]'
required: false
default: patch
type: string
message:
description: 'version commit message to be used'
required: false
default: 'Bumping version %s'
type: string
jobs:
tag-and-publish:
name: "Tag and Publish"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 16
registry-url: https://registry.npmjs.org/
- run: npm version $(semver $npm_package_version -i ${{ inputs.version }}) -m "${{ inputs.message }}"
- run: npm install
- run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
create-release:
name: "Create Release"
needs: tag-and-publish
runs-on: "ubuntu-latest"
steps:
- uses: "marvinpinto/action-automatic-releases@latest"
with:
repo_token: "${{ secrets.GITHUB_TOKEN }}"
automatic_release_tag: "${{ inputs.version }}"
prerelease: false

3
.gitignore vendored
View file

@ -8,4 +8,5 @@ coverage/
test-report.xml test-report.xml
.idea/ .idea/
.vscode/ .vscode/
dist/ build/
# dist/

View file

@ -1,4 +1,4 @@
#!/usr/bin/env sh #!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh" . "$(dirname -- "$0")/_/husky.sh"
npm run lint && npm run build && git add build npm run lint && npm run build && git add dist

View file

@ -0,0 +1,73 @@
<h1 align="center">BPER: Git Backporter</h1>
<h2 align="center">:outbox_tray: :inbox_tray:</h2>
<p align="center">
<a href="https://github.com/lampajr/backporting">
<img alt="CI Checks Status" src="https://github.com/lampajr/backporting/actions/workflows/ci.yml/badge.svg">
</a>
</p>
---
**BPer** is a [NodeJS](https://nodejs.org/) command line tool that provides capabilities to *backport* [1] pull requests in an automated way. This tool also comes with a predefined GitHub action in order to make CI/CD integration easier for all users.
[1] *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.
Table of content
----------------
* **[Usage](#usage)**
* **[GitHub Action](#github-action)**
* **[Limitations](#limitations)**
* **[Contributions](#contributions)**
## Usage
### Inputs
## GitHub Action
This action can be used in any GitHub workflow, below you can find a simple example of manually triggered workflow backporting a specific pull request (provided as input).
```yml
name: Pull Request Backporting using BPer
on:
workflow_dispatch:
inputs:
targetBranch:
description: 'Target branch'
required: true
type: string
pullRequest:
description: 'Pull request'
required: true
type: string
dryRun:
description: 'Dry run'
required: false
default: "true"
type: string
jobs:
backporting:
name: "Backporting"
runs-on: ubuntu-latest
steps:
- name: Backporting
uses: lampajr/backporting@main
with:
target-branch: ${{ inputs.targetBranch }}
pull-request: ${{ inputs.pullRequest }}
auth: ${{ secrets.GITHUB_TOKEN }}
dry-run: ${{ inputs.dryRun }}
```
You can also use this action with other events - you'll just need to specify `target-branch` and `pull-request` params.
For a complete description of all inputs see [Inputs section](#inputs).
## Limitations
## Contributions

25
action.yml Normal file
View file

@ -0,0 +1,25 @@
name: "Backporting GitHub Action"
description: "GitHub action providing an automated way to backport pull requests from one branch to another"
inputs:
dry-run:
description: "If enabled the tool does not create any pull request nor push anything remotely."
required: false
default: "false"
auth:
description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)."
default: ${{ github.token }}
required: false
pull-request:
description: "URL of the pull request to backport, e.g., https://github.com/lampajr/backporting/pull/1."
required: true
target-branch:
description: "Branch where the pull request must be backported to."
required: true
runs:
using: node16
main: dist/gha/index.js
branding:
icon: 'git-merge'
color: 'blue'

View file

@ -10,8 +10,10 @@ const fs_1 = __importDefault(require("fs"));
* Command line git commands executor service * Command line git commands executor service
*/ */
class GitCLIService { class GitCLIService {
constructor() { constructor(auth, author) {
this.logger = logger_service_factory_1.default.getLogger(); this.logger = logger_service_factory_1.default.getLogger();
this.auth = auth;
this.author = author;
} }
/** /**
* Return a pre-configured SimpleGit instance able to execute commands from current * Return a pre-configured SimpleGit instance able to execute commands from current
@ -21,7 +23,18 @@ class GitCLIService {
*/ */
git(cwd) { git(cwd) {
const gitConfig = { ...(cwd ? { baseDir: cwd } : {}) }; const gitConfig = { ...(cwd ? { baseDir: cwd } : {}) };
return (0, simple_git_1.default)(gitConfig).addConfig("user.name", "Github").addConfig("user.email", "noreply@github.com"); return (0, simple_git_1.default)(gitConfig).addConfig("user.name", this.author).addConfig("user.email", "noreply@github.com");
}
/**
* Update the provided remote URL by adding the auth token if not empty
* @param remoteURL remote link, e.g., https://github.com/lampajr/backporting-example.git
*/
remoteWithAuth(remoteURL) {
if (this.auth && this.author) {
return remoteURL.replace("://", `://${this.author}:${this.auth}@`);
}
// return remote as it is
return remoteURL;
} }
/** /**
* Return the git version * Return the git version
@ -39,9 +52,9 @@ class GitCLIService {
* @param branch branch which should be cloned * @param branch branch which should be cloned
*/ */
async clone(from, to, branch) { async clone(from, to, branch) {
this.logger.info(`Cloning repository ${from}..`); this.logger.info(`Cloning repository ${from} to ${to}.`);
if (!fs_1.default.existsSync(to)) { if (!fs_1.default.existsSync(to)) {
await this.git().clone(from, to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]); await (0, simple_git_1.default)().clone(this.remoteWithAuth(from), to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
} }
else { else {
this.logger.warn(`Folder ${to} already exist. Won't clone`); this.logger.warn(`Folder ${to} already exist. Won't clone`);
@ -53,7 +66,7 @@ class GitCLIService {
* @param newBranch new branch name * @param newBranch new branch name
*/ */
async createLocalBranch(cwd, newBranch) { async createLocalBranch(cwd, newBranch) {
this.logger.info(`Creating branch ${newBranch}..`); this.logger.info(`Creating branch ${newBranch}.`);
await this.git(cwd).checkoutLocalBranch(newBranch); await this.git(cwd).checkoutLocalBranch(newBranch);
} }
/** /**
@ -63,8 +76,8 @@ class GitCLIService {
* @param remoteName [optional] name of the remote, by default 'fork' is used * @param remoteName [optional] name of the remote, by default 'fork' is used
*/ */
async addRemote(cwd, remote, remoteName = "fork") { async addRemote(cwd, remote, remoteName = "fork") {
this.logger.info(`Adding new remote ${remote}..`); this.logger.info(`Adding new remote ${remote}.`);
await this.git(cwd).addRemote(remoteName, remote); await this.git(cwd).addRemote(remoteName, this.remoteWithAuth(remote));
} }
/** /**
* Git fetch from a particular branch * Git fetch from a particular branch
@ -81,8 +94,8 @@ class GitCLIService {
* @param sha commit sha * @param sha commit sha
*/ */
async cherryPick(cwd, sha) { async cherryPick(cwd, sha) {
this.logger.info(`Cherry picking ${sha}..`); this.logger.info(`Cherry picking ${sha}.`);
await this.git(cwd).raw(["cherry-pick", "--strategy=recursive", "-X", "theirs", sha]); await this.git(cwd).raw(["cherry-pick", "-m", "1", "--strategy=recursive", "--strategy-option=theirs", sha]);
} }
/** /**
* Push a branch to a remote * Push a branch to a remote
@ -91,7 +104,7 @@ class GitCLIService {
* @param remote [optional] remote to which the branch should be pushed to, by default 'origin' * @param remote [optional] remote to which the branch should be pushed to, by default 'origin'
*/ */
async push(cwd, branch, remote = "origin", force = false) { async push(cwd, branch, remote = "origin", force = false) {
this.logger.info(`Pushing ${branch} to ${remote}..`); this.logger.info(`Pushing ${branch} to ${remote}.`);
const options = ["--quiet"]; const options = ["--quiet"];
if (force) { if (force) {
options.push("--force-with-lease"); options.push("--force-with-lease");

View file

@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const git_types_1 = require("../../service/git/git.types"); const git_types_1 = require("../../service/git/git.types");
const github_service_1 = __importDefault(require("../../service/git/github/github-service")); const github_service_1 = __importDefault(require("../../service/git/github/github-service"));
const logger_service_factory_1 = __importDefault(require("../../service/logger/logger-service-factory"));
/** /**
* Singleton git service factory class * Singleton git service factory class
*/ */
@ -22,7 +23,8 @@ class GitServiceFactory {
*/ */
static init(type, auth) { static init(type, auth) {
if (GitServiceFactory.instance) { if (GitServiceFactory.instance) {
throw new Error("Git service already initialized!"); GitServiceFactory.logger.warn("Git service already initialized!");
return;
} }
switch (type) { switch (type) {
case git_types_1.GitServiceType.GITHUB: case git_types_1.GitServiceType.GITHUB:
@ -34,3 +36,4 @@ class GitServiceFactory {
} }
} }
exports.default = GitServiceFactory; exports.default = GitServiceFactory;
GitServiceFactory.logger = logger_service_factory_1.default.getLogger();

View file

@ -3,14 +3,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
class GitHubMapper { class GitHubMapper {
mapPullRequest(pr) { mapPullRequest(pr) {
return { return {
author: pr.user.login,
url: pr.url, url: pr.url,
htmlUrl: pr.html_url,
title: pr.title, title: pr.title,
body: pr.body, body: pr.body ?? "",
patchUrl: pr.patch_url,
state: pr.state, state: pr.state,
merged: pr.merged ?? false,
mergedBy: pr.merged_by?.login,
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => r?.login)), reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => r?.login)),
sourceRepo: pr.head.repo.full_name, sourceRepo: {
targetRepo: pr.base.repo.full_name, owner: pr.head.repo.full_name.split("/")[0],
project: pr.head.repo.full_name.split("/")[1],
cloneUrl: pr.head.repo.clone_url
},
targetRepo: {
owner: pr.base.repo.full_name.split("/")[0],
project: pr.base.repo.full_name.split("/")[1],
cloneUrl: pr.base.repo.clone_url
},
commits: [pr.merge_commit_sha] commits: [pr.merge_commit_sha]
}; };
} }

View file

@ -5,13 +5,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
const github_mapper_1 = __importDefault(require("../../../service/git/github/github-mapper")); const github_mapper_1 = __importDefault(require("../../../service/git/github/github-mapper"));
const octokit_factory_1 = __importDefault(require("../../../service/git/github/octokit-factory")); const octokit_factory_1 = __importDefault(require("../../../service/git/github/octokit-factory"));
const logger_service_factory_1 = __importDefault(require("../../../service/logger/logger-service-factory"));
class GitHubService { class GitHubService {
constructor(token) { constructor(token) {
this.logger = logger_service_factory_1.default.getLogger();
this.octokit = octokit_factory_1.default.getOctokit(token); this.octokit = octokit_factory_1.default.getOctokit(token);
this.mapper = new github_mapper_1.default(); this.mapper = new github_mapper_1.default();
} }
// READ // READ
async getPullRequest(owner, repo, prNumber) { async getPullRequest(owner, repo, prNumber) {
this.logger.info(`Getting pull request ${owner}/${repo}/${prNumber}.`);
const { data } = await this.octokit.rest.pulls.get({ const { data } = await this.octokit.rest.pulls.get({
owner: owner, owner: owner,
repo: repo, repo: repo,
@ -19,12 +22,48 @@ class GitHubService {
}); });
return this.mapper.mapPullRequest(data); return this.mapper.mapPullRequest(data);
} }
async getPullRequestFromUrl(prUrl) {
const { owner, project } = this.getRepositoryFromPrUrl(prUrl);
return this.getPullRequest(owner, project, parseInt(prUrl.substring(prUrl.lastIndexOf("/") + 1, prUrl.length)));
}
// WRITE // WRITE
// eslint-disable-next-line @typescript-eslint/no-unused-vars async createPullRequest(backport) {
createPullRequest(owner, repo, head, base, title, body, reviewers) { this.logger.info(`Creating pull request ${backport.head} -> ${backport.base}.`);
// throw new Error("Method not implemented."); this.logger.info(`${JSON.stringify(backport, null, 2)}`);
// TODO implement const { data } = await this.octokit.pulls.create({
return Promise.resolve(); owner: backport.owner,
repo: backport.repo,
head: backport.head,
base: backport.base,
title: backport.title,
body: backport.body
});
if (backport.reviewers.length > 0) {
try {
await this.octokit.pulls.requestReviewers({
owner: backport.owner,
repo: backport.repo,
pull_number: data.number,
reviewers: backport.reviewers
});
}
catch (error) {
this.logger.error(`Error requesting reviewers: ${error}`);
}
}
}
// UTILS
/**
* Extract repository owner and project from the pull request url
* @param prUrl pull request url
* @returns {{owner: string, project: string}}
*/
getRepositoryFromPrUrl(prUrl) {
const elems = prUrl.split("/");
return {
owner: elems[elems.length - 4],
project: elems[elems.length - 3]
};
} }
} }
exports.default = GitHubService; exports.default = GitHubService;

View file

@ -11,7 +11,7 @@ const rest_1 = require("@octokit/rest");
class OctokitFactory { class OctokitFactory {
static getOctokit(token) { static getOctokit(token) {
if (!OctokitFactory.octokit) { if (!OctokitFactory.octokit) {
OctokitFactory.logger.info("Creating octokit instance.."); OctokitFactory.logger.info("Creating octokit instance.");
OctokitFactory.octokit = new rest_1.Octokit({ OctokitFactory.octokit = new rest_1.Octokit({
auth: token, auth: token,
userAgent: "lampajr/backporting" userAgent: "lampajr/backporting"

View file

@ -19,9 +19,17 @@ describe("github service", () => {
gitService = git_service_factory_1.default.getService(); gitService = git_service_factory_1.default.getService();
}); });
test("get pull request: success", async () => { test("get pull request: success", async () => {
const res = await gitService.getPullRequest(moctokit_data_1.targetOwner, moctokit_data_1.repo, moctokit_data_1.pullRequestNumber); const res = await gitService.getPullRequest(moctokit_data_1.targetOwner, moctokit_data_1.repo, moctokit_data_1.mergedPullRequestFixture.number);
expect(res.sourceRepo).toBe("fork/reponame"); expect(res.sourceRepo).toEqual({
expect(res.targetRepo).toBe("owner/reponame"); owner: "fork",
project: "reponame",
cloneUrl: "https://github.com/fork/reponame.git"
});
expect(res.targetRepo).toEqual({
owner: "owner",
project: "reponame",
cloneUrl: "https://github.com/owner/reponame.git"
});
expect(res.title).toBe("PR Title"); expect(res.title).toBe("PR Title");
expect(res.commits.length).toBe(1); expect(res.commits.length).toBe(1);
expect(res.commits).toEqual(["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]); expect(res.commits).toEqual(["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]);

View file

@ -1,13 +1,11 @@
"use strict"; "use strict";
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.validPR = exports.commitRef = exports.invalidPullRequestNumber = exports.pullRequestNumber = exports.repo = exports.sourceOwner = exports.targetOwner = void 0; exports.notMergedPullRequestFixture = exports.mergedPullRequestFixture = exports.notFoundPullRequestNumber = exports.repo = exports.sourceOwner = exports.targetOwner = void 0;
exports.targetOwner = "owner"; exports.targetOwner = "owner";
exports.sourceOwner = "fork"; exports.sourceOwner = "fork";
exports.repo = "reponame"; exports.repo = "reponame";
exports.pullRequestNumber = 2368; exports.notFoundPullRequestNumber = 1;
exports.invalidPullRequestNumber = 1; exports.mergedPullRequestFixture = {
exports.commitRef = "91748965051fae1330ad58d15cf694e103267c87";
exports.validPR = {
"url": "https://api.github.com/repos/owner/reponame/pulls/2368", "url": "https://api.github.com/repos/owner/reponame/pulls/2368",
"id": 1137188271, "id": 1137188271,
"node_id": "PR_kwDOABTq6s5DyB2v", "node_id": "PR_kwDOABTq6s5DyB2v",
@ -20,22 +18,22 @@ exports.validPR = {
"locked": false, "locked": false,
"title": "PR Title", "title": "PR Title",
"user": { "user": {
"login": "kie-ci", "login": "gh-user",
"id": 11995863, "id": 11995863,
"node_id": "MDQ6VXNlcjExOTk1ODYz", "node_id": "MDQ6VXNlcjExOTk1ODYz",
"avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/kie-ci", "url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/kie-ci", "html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/kie-ci/followers", "followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/kie-ci/following{/other_user}", "following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/kie-ci/gists{/gist_id}", "gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kie-ci/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kie-ci/subscriptions", "subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/kie-ci/orgs", "organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/kie-ci/repos", "repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/kie-ci/events{/privacy}", "events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/kie-ci/received_events", "received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
}, },
@ -49,22 +47,42 @@ exports.validPR = {
"assignees": [], "assignees": [],
"requested_reviewers": [ "requested_reviewers": [
{ {
"login": "ghuser", "login": "requested-gh-user",
"id": 1422582, "id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=", "node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/ghuser", "url": "https://api.github.com/users/requested-gh-user",
"html_url": "https://github.com/ghuser", "html_url": "https://github.com/requested-gh-user",
"followers_url": "https://api.github.com/users/ghuser/followers", "followers_url": "https://api.github.com/users/requested-gh-user/followers",
"following_url": "https://api.github.com/users/ghuser/following{/other_user}", "following_url": "https://api.github.com/users/requested-gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/ghuser/gists{/gist_id}", "gists_url": "https://api.github.com/users/requested-gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ghuser/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/requested-gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ghuser/subscriptions", "subscriptions_url": "https://api.github.com/users/requested-gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/ghuser/orgs", "organizations_url": "https://api.github.com/users/requested-gh-user/orgs",
"repos_url": "https://api.github.com/users/ghuser/repos", "repos_url": "https://api.github.com/users/requested-gh-user/repos",
"events_url": "https://api.github.com/users/ghuser/events{/privacy}", "events_url": "https://api.github.com/users/requested-gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/ghuser/received_events", "received_events_url": "https://api.github.com/users/requested-gh-user/received_events",
"type": "User",
"site_admin": false
},
{
"login": "gh-user",
"id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
} }
@ -105,7 +123,7 @@ exports.validPR = {
"repo": { "repo": {
"id": 1370858, "id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4", "node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "optaplanner", "name": "reponame",
"full_name": "fork/reponame", "full_name": "fork/reponame",
"private": false, "private": false,
"owner": { "owner": {
@ -175,7 +193,7 @@ exports.validPR = {
"ssh_url": "git@github.com:fork/reponame.git", "ssh_url": "git@github.com:fork/reponame.git",
"clone_url": "https://github.com/fork/reponame.git", "clone_url": "https://github.com/fork/reponame.git",
"svn_url": "https://github.com/fork/reponame", "svn_url": "https://github.com/fork/reponame",
"homepage": "https://www.optaplanner.org", "homepage": "https://www.reponame.org",
"size": 238339, "size": 238339,
"stargazers_count": 2811, "stargazers_count": 2811,
"watchers_count": 2811, "watchers_count": 2811,
@ -257,7 +275,7 @@ exports.validPR = {
"repo": { "repo": {
"id": 1370858, "id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4", "node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "optaplanner", "name": "reponame",
"full_name": "owner/reponame", "full_name": "owner/reponame",
"private": false, "private": false,
"owner": { "owner": {
@ -327,7 +345,7 @@ exports.validPR = {
"ssh_url": "git@github.com:owner/reponame.git", "ssh_url": "git@github.com:owner/reponame.git",
"clone_url": "https://github.com/owner/reponame.git", "clone_url": "https://github.com/owner/reponame.git",
"svn_url": "https://github.com/owner/reponame", "svn_url": "https://github.com/owner/reponame",
"homepage": "https://www.optaplanner.org", "homepage": "https://www.reponame.org",
"size": 238339, "size": 238339,
"stargazers_count": 2811, "stargazers_count": 2811,
"watchers_count": 2811, "watchers_count": 2811,
@ -416,22 +434,458 @@ exports.validPR = {
"rebaseable": null, "rebaseable": null,
"mergeable_state": "unknown", "mergeable_state": "unknown",
"merged_by": { "merged_by": {
"login": "radtriste", "login": "that-s-a-user",
"id": 17157711, "id": 17157711,
"node_id": "MDQ6VXNlcjE3MTU3NzEx", "node_id": "MDQ6VXNlcjE3MTU3NzEx",
"avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/radtriste", "url": "https://api.github.com/users/that-s-a-user",
"html_url": "https://github.com/radtriste", "html_url": "https://github.com/that-s-a-user",
"followers_url": "https://api.github.com/users/radtriste/followers", "followers_url": "https://api.github.com/users/that-s-a-user/followers",
"following_url": "https://api.github.com/users/radtriste/following{/other_user}", "following_url": "https://api.github.com/users/that-s-a-user/following{/other_user}",
"gists_url": "https://api.github.com/users/radtriste/gists{/gist_id}", "gists_url": "https://api.github.com/users/that-s-a-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/radtriste/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/that-s-a-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/radtriste/subscriptions", "subscriptions_url": "https://api.github.com/users/that-s-a-user/subscriptions",
"organizations_url": "https://api.github.com/users/radtriste/orgs", "organizations_url": "https://api.github.com/users/that-s-a-user/orgs",
"repos_url": "https://api.github.com/users/radtriste/repos", "repos_url": "https://api.github.com/users/that-s-a-user/repos",
"events_url": "https://api.github.com/users/radtriste/events{/privacy}", "events_url": "https://api.github.com/users/that-s-a-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/radtriste/received_events", "received_events_url": "https://api.github.com/users/that-s-a-user/received_events",
"type": "User",
"site_admin": false
},
"comments": 0,
"review_comments": 0,
"maintainer_can_modify": false,
"commits": 2,
"additions": 2,
"deletions": 2,
"changed_files": 2
};
exports.notMergedPullRequestFixture = {
"url": "https://api.github.com/repos/owner/reponame/pulls/4444",
"id": 1137188271,
"node_id": "PR_kwDOABTq6s5DyB2v",
"html_url": "https://github.com/owner/reponame/pull/4444",
"diff_url": "https://github.com/owner/reponame/pull/4444.diff",
"patch_url": "https://github.com/owner/reponame/pull/4444.patch",
"issue_url": "https://api.github.com/repos/owner/reponame/issues/4444",
"number": 4444,
"state": "closed",
"locked": false,
"title": "PR Title",
"user": {
"login": "gh-user",
"id": 11995863,
"node_id": "MDQ6VXNlcjExOTk1ODYz",
"avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User",
"site_admin": false
},
"body": "Please review and merge",
"created_at": "2022-11-28T08:43:09Z",
"updated_at": "2022-11-28T10:11:53Z",
"closed_at": "2022-11-28T10:11:52Z",
"merged_at": "2022-11-28T10:11:52Z",
"merge_commit_sha": "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
"assignee": null,
"assignees": [],
"requested_reviewers": [
{
"login": "gh-user",
"id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User",
"site_admin": false
}
],
"requested_teams": [],
"labels": [],
"milestone": null,
"draft": false,
"commits_url": "https://api.github.com/repos/owner/reponame/pulls/4444/commits",
"review_comments_url": "https://api.github.com/repos/owner/reponame/pulls/4444/comments",
"review_comment_url": "https://api.github.com/repos/owner/reponame/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/owner/reponame/issues/4444/comments",
"statuses_url": "https://api.github.com/repos/owner/reponame/statuses/91748965051fae1330ad58d15cf694e103267c87",
"head": {
"label": "kiegroup:bump-8.31.x-drools-8.31.0.Final",
"ref": "bump-8.31.x-drools-8.31.0.Final",
"sha": "91748965051fae1330ad58d15cf694e103267c87",
"user": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"repo": {
"id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "reponame",
"full_name": "fork/reponame",
"private": false,
"owner": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"html_url": "https://github.com/fork/reponame",
"description": "AI constraint solver in Java to optimize the vehicle routing problem, employee rostering, task assignment, maintenance scheduling, conference scheduling and other planning problems.",
"fork": false,
"url": "https://api.github.com/repos/fork/reponame",
"forks_url": "https://api.github.com/repos/fork/reponame/forks",
"keys_url": "https://api.github.com/repos/fork/reponame/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/fork/reponame/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/fork/reponame/teams",
"hooks_url": "https://api.github.com/repos/fork/reponame/hooks",
"issue_events_url": "https://api.github.com/repos/fork/reponame/issues/events{/number}",
"events_url": "https://api.github.com/repos/fork/reponame/events",
"assignees_url": "https://api.github.com/repos/fork/reponame/assignees{/user}",
"branches_url": "https://api.github.com/repos/fork/reponame/branches{/branch}",
"tags_url": "https://api.github.com/repos/fork/reponame/tags",
"blobs_url": "https://api.github.com/repos/fork/reponame/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/fork/reponame/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/fork/reponame/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/fork/reponame/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/fork/reponame/statuses/{sha}",
"languages_url": "https://api.github.com/repos/fork/reponame/languages",
"stargazers_url": "https://api.github.com/repos/fork/reponame/stargazers",
"contributors_url": "https://api.github.com/repos/fork/reponame/contributors",
"subscribers_url": "https://api.github.com/repos/fork/reponame/subscribers",
"subscription_url": "https://api.github.com/repos/fork/reponame/subscription",
"commits_url": "https://api.github.com/repos/fork/reponame/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/fork/reponame/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/fork/reponame/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/fork/reponame/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/fork/reponame/contents/{+path}",
"compare_url": "https://api.github.com/repos/fork/reponame/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/fork/reponame/merges",
"archive_url": "https://api.github.com/repos/fork/reponame/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/fork/reponame/downloads",
"issues_url": "https://api.github.com/repos/fork/reponame/issues{/number}",
"pulls_url": "https://api.github.com/repos/fork/reponame/pulls{/number}",
"milestones_url": "https://api.github.com/repos/fork/reponame/milestones{/number}",
"notifications_url": "https://api.github.com/repos/fork/reponame/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/fork/reponame/labels{/name}",
"releases_url": "https://api.github.com/repos/fork/reponame/releases{/id}",
"deployments_url": "https://api.github.com/repos/fork/reponame/deployments",
"created_at": "2011-02-15T19:38:23Z",
"updated_at": "2022-11-28T05:01:47Z",
"pushed_at": "2022-11-28T10:50:51Z",
"git_url": "git://github.com/fork/reponame.git",
"ssh_url": "git@github.com:fork/reponame.git",
"clone_url": "https://github.com/fork/reponame.git",
"svn_url": "https://github.com/fork/reponame",
"homepage": "https://www.reponame.org",
"size": 238339,
"stargazers_count": 2811,
"watchers_count": 2811,
"language": "Java",
"has_issues": false,
"has_projects": false,
"has_downloads": true,
"has_wiki": false,
"has_pages": false,
"has_discussions": false,
"forks_count": 878,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 30,
"license": {
"key": "apache-2.0",
"name": "Apache License 2.0",
"spdx_id": "Apache-2.0",
"url": "https://api.github.com/licenses/apache-2.0",
"node_id": "MDc6TGljZW5zZTI="
},
"allow_forking": true,
"is_template": false,
"web_commit_signoff_required": false,
"topics": [
"artificial-intelligence",
"branch-and-bound",
"constraint-programming",
"constraint-satisfaction-problem",
"constraint-solver",
"constraints",
"employee-rostering",
"java",
"local-search",
"mathematical-optimization",
"metaheuristics",
"optimization",
"rostering",
"scheduling",
"simulated-annealing",
"solver",
"tabu-search",
"traveling-salesman",
"traveling-salesman-problem",
"vehicle-routing-problem"
],
"visibility": "public",
"forks": 878,
"open_issues": 30,
"watchers": 2811,
"default_branch": "main"
}
},
"base": {
"label": "kiegroup:8.31.x",
"ref": "8.31.x",
"sha": "8cfc286765cb01c84a1d62c65519fa8032bfecbd",
"user": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"repo": {
"id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "reponame",
"full_name": "owner/reponame",
"private": false,
"owner": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"html_url": "https://github.com/owner/reponame",
"description": "AI constraint solver in Java to optimize the vehicle routing problem, employee rostering, task assignment, maintenance scheduling, conference scheduling and other planning problems.",
"fork": false,
"url": "https://api.github.com/repos/owner/reponame",
"forks_url": "https://api.github.com/repos/owner/reponame/forks",
"keys_url": "https://api.github.com/repos/owner/reponame/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/owner/reponame/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/owner/reponame/teams",
"hooks_url": "https://api.github.com/repos/owner/reponame/hooks",
"issue_events_url": "https://api.github.com/repos/owner/reponame/issues/events{/number}",
"events_url": "https://api.github.com/repos/owner/reponame/events",
"assignees_url": "https://api.github.com/repos/owner/reponame/assignees{/user}",
"branches_url": "https://api.github.com/repos/owner/reponame/branches{/branch}",
"tags_url": "https://api.github.com/repos/owner/reponame/tags",
"blobs_url": "https://api.github.com/repos/owner/reponame/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/owner/reponame/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/owner/reponame/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/owner/reponame/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/owner/reponame/statuses/{sha}",
"languages_url": "https://api.github.com/repos/owner/reponame/languages",
"stargazers_url": "https://api.github.com/repos/owner/reponame/stargazers",
"contributors_url": "https://api.github.com/repos/owner/reponame/contributors",
"subscribers_url": "https://api.github.com/repos/owner/reponame/subscribers",
"subscription_url": "https://api.github.com/repos/owner/reponame/subscription",
"commits_url": "https://api.github.com/repos/owner/reponame/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/owner/reponame/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/owner/reponame/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/owner/reponame/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/owner/reponame/contents/{+path}",
"compare_url": "https://api.github.com/repos/owner/reponame/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/owner/reponame/merges",
"archive_url": "https://api.github.com/repos/owner/reponame/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/owner/reponame/downloads",
"issues_url": "https://api.github.com/repos/owner/reponame/issues{/number}",
"pulls_url": "https://api.github.com/repos/owner/reponame/pulls{/number}",
"milestones_url": "https://api.github.com/repos/owner/reponame/milestones{/number}",
"notifications_url": "https://api.github.com/repos/owner/reponame/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/owner/reponame/labels{/name}",
"releases_url": "https://api.github.com/repos/owner/reponame/releases{/id}",
"deployments_url": "https://api.github.com/repos/owner/reponame/deployments",
"created_at": "2011-02-15T19:38:23Z",
"updated_at": "2022-11-28T05:01:47Z",
"pushed_at": "2022-11-28T10:50:51Z",
"git_url": "git://github.com/owner/reponame.git",
"ssh_url": "git@github.com:owner/reponame.git",
"clone_url": "https://github.com/owner/reponame.git",
"svn_url": "https://github.com/owner/reponame",
"homepage": "https://www.reponame.org",
"size": 238339,
"stargazers_count": 2811,
"watchers_count": 2811,
"language": "Java",
"has_issues": false,
"has_projects": false,
"has_downloads": true,
"has_wiki": false,
"has_pages": false,
"has_discussions": false,
"forks_count": 878,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 30,
"license": {
"key": "apache-2.0",
"name": "Apache License 2.0",
"spdx_id": "Apache-2.0",
"url": "https://api.github.com/licenses/apache-2.0",
"node_id": "MDc6TGljZW5zZTI="
},
"allow_forking": true,
"is_template": false,
"web_commit_signoff_required": false,
"topics": [
"artificial-intelligence",
"branch-and-bound",
"constraint-programming",
"constraint-satisfaction-problem",
"constraint-solver",
"constraints",
"employee-rostering",
"java",
"local-search",
"mathematical-optimization",
"metaheuristics",
"optimization",
"rostering",
"scheduling",
"simulated-annealing",
"solver",
"tabu-search",
"traveling-salesman",
"traveling-salesman-problem",
"vehicle-routing-problem"
],
"visibility": "public",
"forks": 878,
"open_issues": 30,
"watchers": 2811,
"default_branch": "main"
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444"
},
"html": {
"href": "https://github.com/owner/reponame/pull/4444"
},
"issue": {
"href": "https://api.github.com/repos/owner/reponame/issues/4444"
},
"comments": {
"href": "https://api.github.com/repos/owner/reponame/issues/4444/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/owner/reponame/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444/commits"
},
"statuses": {
"href": "https://api.github.com/repos/owner/reponame/statuses/91748965051fae1330ad58d15cf694e103267c87"
}
},
"author_association": "CONTRIBUTOR",
"auto_merge": null,
"active_lock_reason": null,
"merged": null,
"mergeable": null,
"rebaseable": null,
"mergeable_state": "unknown",
"merged_by": {
"login": "that-s-a-user",
"id": 17157711,
"node_id": "MDQ6VXNlcjE3MTU3NzEx",
"avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/that-s-a-user",
"html_url": "https://github.com/that-s-a-user",
"followers_url": "https://api.github.com/users/that-s-a-user/followers",
"following_url": "https://api.github.com/users/that-s-a-user/following{/other_user}",
"gists_url": "https://api.github.com/users/that-s-a-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/that-s-a-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/that-s-a-user/subscriptions",
"organizations_url": "https://api.github.com/users/that-s-a-user/orgs",
"repos_url": "https://api.github.com/users/that-s-a-user/repos",
"events_url": "https://api.github.com/users/that-s-a-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/that-s-a-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
}, },

View file

@ -9,7 +9,7 @@ const mock_github_1 = require("@kie/mock-github");
const moctokit_data_1 = require("./moctokit-data"); const moctokit_data_1 = require("./moctokit-data");
const logger = logger_service_factory_1.default.getLogger(); const logger = logger_service_factory_1.default.getLogger();
const setupMoctokit = () => { const setupMoctokit = () => {
logger.debug("Setting up moctokit.."); logger.debug("Setting up moctokit.");
const mock = new mock_github_1.Moctokit(); const mock = new mock_github_1.Moctokit();
// setup the mock requests here // setup the mock requests here
// valid requests // valid requests
@ -17,24 +17,40 @@ const setupMoctokit = () => {
.get({ .get({
owner: moctokit_data_1.targetOwner, owner: moctokit_data_1.targetOwner,
repo: moctokit_data_1.repo, repo: moctokit_data_1.repo,
pull_number: moctokit_data_1.pullRequestNumber pull_number: moctokit_data_1.mergedPullRequestFixture.number
}) })
.reply({ .reply({
status: 200, status: 200,
data: moctokit_data_1.validPR data: moctokit_data_1.mergedPullRequestFixture
});
mock.rest.pulls
.get({
owner: moctokit_data_1.targetOwner,
repo: moctokit_data_1.repo,
pull_number: moctokit_data_1.notMergedPullRequestFixture.number
})
.reply({
status: 200,
data: moctokit_data_1.notMergedPullRequestFixture
}); });
mock.rest.pulls mock.rest.pulls
.create() .create()
.reply({ .reply({
status: 201, status: 201,
data: moctokit_data_1.validPR data: moctokit_data_1.mergedPullRequestFixture
});
mock.rest.pulls
.requestReviewers()
.reply({
status: 201,
data: moctokit_data_1.mergedPullRequestFixture
}); });
// invalid requests // invalid requests
mock.rest.pulls mock.rest.pulls
.get({ .get({
owner: moctokit_data_1.targetOwner, owner: moctokit_data_1.targetOwner,
repo: moctokit_data_1.repo, repo: moctokit_data_1.repo,
pull_number: moctokit_data_1.invalidPullRequestNumber pull_number: moctokit_data_1.notFoundPullRequestNumber
}) })
.reply({ .reply({
status: 404, status: 404,

16112
dist/cli/index.js vendored Executable file

File diff suppressed because one or more lines are too long

15542
dist/gha/index.js vendored Executable file

File diff suppressed because one or more lines are too long

View file

@ -6,11 +6,11 @@ const jestConfig: Config.InitialOptions = {
"^.+\\.tsx?$": "ts-jest", "^.+\\.tsx?$": "ts-jest",
}, },
moduleNameMapper: { moduleNameMapper: {
"^@gb/(.*)$": "<rootDir>/src/$1", "^@bp/(.*)$": "<rootDir>/src/$1",
}, },
clearMocks: true, clearMocks: true,
resetMocks: true, resetMocks: true,
modulePathIgnorePatterns: ["<rootDir>/dist/"], modulePathIgnorePatterns: ["<rootDir>/build/", "<rootDir>/dist/"],
coveragePathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/test/", "<rootDir>/build/", "<rootDir>/dist/"] coveragePathIgnorePatterns: ["<rootDir>/node_modules/", "<rootDir>/test/", "<rootDir>/build/", "<rootDir>/dist/"]
}; };
export default jestConfig; export default jestConfig;

608
package-lock.json generated
View file

@ -1,57 +1,45 @@
{ {
"name": "@lampajr/backporting", "name": "@lampajr/bper",
"version": "0.0.1", "version": "1.0.0",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@lampajr/backporting", "name": "@lampajr/bper",
"version": "0.0.1", "version": "1.0.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/artifact": "^1.1.0", "@actions/core": "^1.10.0",
"@actions/core": "^1.8.2",
"@actions/exec": "^1.1.1",
"@actions/glob": "^0.3.0",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.5",
"@octokit/types": "^8.0.0", "@octokit/types": "^8.0.0",
"@octokit/webhooks-types": "^6.7.0", "@octokit/webhooks-types": "^6.7.0",
"commander": "^9.3.0", "commander": "^9.3.0",
"fs-extra": "^10.1.0", "fs-extra": "^11.1.0",
"simple-git": "^3.15.1" "simple-git": "^3.15.1"
}, },
"bin": { "bin": {
"backporting": "build/src/bin/main.js" "bper": "dist/cli/index.js"
}, },
"devDependencies": { "devDependencies": {
"@kie/mock-github": "^0.1.1", "@kie/mock-github": "^0.1.2",
"@types/fs-extra": "^9.0.13", "@types/fs-extra": "^9.0.13",
"@types/jest": "^28.1.8", "@types/jest": "^28.1.8",
"@types/node": "^17.0.41", "@types/node": "^18.11.17",
"@typescript-eslint/eslint-plugin": "^5.45.1", "@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.45.1", "@typescript-eslint/parser": "^5.45.1",
"@vercel/ncc": "^0.36.0",
"eslint": "^8.29.0", "eslint": "^8.29.0",
"husky": "^8.0.2", "husky": "^8.0.2",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-sonar-reporter": "^2.0.0", "jest-sonar-reporter": "^2.0.0",
"semver": "^7.3.8",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"ts-node": "^10.8.1", "ts-node": "^10.8.1",
"tsc-alias": "^1.7.1", "tsc-alias": "^1.8.2",
"tsconfig-paths": "^4.1.0", "tsconfig-paths": "^4.1.0",
"typescript": "^4.9.3" "typescript": "^4.9.3"
} }
}, },
"node_modules/@actions/artifact": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-1.1.0.tgz",
"integrity": "sha512-shO+w/BAnzRnFhfsgUao8sxjByAMqDdfvek2LLKeCueBKXoTrAcp7U/hs9Fdx+z9g7Q0mbIrmHAzAAww4HK1bQ==",
"dependencies": {
"@actions/core": "^1.2.6",
"@actions/http-client": "^2.0.1",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2"
}
},
"node_modules/@actions/core": { "node_modules/@actions/core": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
@ -61,23 +49,6 @@
"uuid": "^8.3.2" "uuid": "^8.3.2"
} }
}, },
"node_modules/@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"dependencies": {
"@actions/io": "^1.0.1"
}
},
"node_modules/@actions/glob": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
"dependencies": {
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"node_modules/@actions/http-client": { "node_modules/@actions/http-client": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
@ -86,11 +57,6 @@
"tunnel": "^0.0.6" "tunnel": "^0.0.6"
} }
}, },
"node_modules/@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
},
"node_modules/@ampproject/remapping": { "node_modules/@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@ -1644,9 +1610,9 @@
} }
}, },
"node_modules/@kie/mock-github": { "node_modules/@kie/mock-github": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-0.1.2.tgz",
"integrity": "sha512-JO9a8ZcuMraTMHavzwIyGy2tl4+44cW3K5A42uTah8uM2HAitXMF47/q4cAFkMWKaL0pu3T8QEcZ+FdgpaTR+w==", "integrity": "sha512-WgJr0LmDye+YVZLmsRFVHlluLGVbRMosa1VKW7Nv1PvytcqHy1pdj8ljRN74uVHl7AcV0JDP/4Qb+wRwC4n18Q==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@octokit/openapi-types-ghec": "^14.0.0", "@octokit/openapi-types-ghec": "^14.0.0",
@ -1659,6 +1625,20 @@
"totalist": "^3.0.0" "totalist": "^3.0.0"
} }
}, },
"node_modules/@kie/mock-github/node_modules/fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/@kwsites/file-exists": { "node_modules/@kwsites/file-exists": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz",
@ -2013,9 +1993,9 @@
"dev": true "dev": true
}, },
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "17.0.45", "version": "18.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==",
"dev": true "dev": true
}, },
"node_modules/@types/prettier": { "node_modules/@types/prettier": {
@ -2052,14 +2032,14 @@
"dev": true "dev": true
}, },
"node_modules/@typescript-eslint/eslint-plugin": { "node_modules/@typescript-eslint/eslint-plugin": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz",
"integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/scope-manager": "5.45.1", "@typescript-eslint/scope-manager": "5.47.0",
"@typescript-eslint/type-utils": "5.45.1", "@typescript-eslint/type-utils": "5.47.0",
"@typescript-eslint/utils": "5.45.1", "@typescript-eslint/utils": "5.47.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ignore": "^5.2.0", "ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0", "natural-compare-lite": "^1.4.0",
@ -2084,6 +2064,53 @@
} }
} }
}, },
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
"integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/parser": { "node_modules/@typescript-eslint/parser": {
"version": "5.45.1", "version": "5.45.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.1.tgz",
@ -2129,13 +2156,13 @@
} }
}, },
"node_modules/@typescript-eslint/type-utils": { "node_modules/@typescript-eslint/type-utils": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz",
"integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@typescript-eslint/typescript-estree": "5.45.1", "@typescript-eslint/typescript-estree": "5.47.0",
"@typescript-eslint/utils": "5.45.1", "@typescript-eslint/utils": "5.47.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"tsutils": "^3.21.0" "tsutils": "^3.21.0"
}, },
@ -2155,6 +2182,63 @@
} }
} }
}, },
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
"integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/types": { "node_modules/@typescript-eslint/types": {
"version": "5.45.1", "version": "5.45.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.1.tgz",
@ -2196,16 +2280,16 @@
} }
}, },
"node_modules/@typescript-eslint/utils": { "node_modules/@typescript-eslint/utils": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz",
"integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12", "@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.45.1", "@typescript-eslint/scope-manager": "5.47.0",
"@typescript-eslint/types": "5.45.1", "@typescript-eslint/types": "5.47.0",
"@typescript-eslint/typescript-estree": "5.45.1", "@typescript-eslint/typescript-estree": "5.47.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0", "eslint-utils": "^3.0.0",
"semver": "^7.3.7" "semver": "^7.3.7"
@ -2221,6 +2305,80 @@
"eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0"
} }
}, },
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
"integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true,
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
"integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependenciesMeta": {
"typescript": {
"optional": true
}
}
},
"node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"dependencies": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
}
},
"node_modules/@typescript-eslint/visitor-keys": { "node_modules/@typescript-eslint/visitor-keys": {
"version": "5.45.1", "version": "5.45.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.1.tgz",
@ -2238,6 +2396,15 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@vercel/ncc": {
"version": "0.36.0",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.0.tgz",
"integrity": "sha512-/ZTUJ/ZkRt694k7KJNimgmHjtQcRuVwsST2Z6XfYveQIuBbHR+EqkTc1jfgPkQmMyk/vtpxo3nVxe8CNuau86A==",
"dev": true,
"bin": {
"ncc": "dist/ncc/cli.js"
}
},
"node_modules/accepts": { "node_modules/accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -2482,7 +2649,8 @@
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
}, },
"node_modules/before-after-hook": { "node_modules/before-after-hook": {
"version": "2.2.3", "version": "2.2.3",
@ -2541,6 +2709,7 @@
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -2807,7 +2976,8 @@
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
}, },
"node_modules/content-disposition": { "node_modules/content-disposition": {
"version": "0.5.4", "version": "0.5.4",
@ -3586,22 +3756,23 @@
} }
}, },
"node_modules/fs-extra": { "node_modules/fs-extra": {
"version": "10.1.0", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
"dependencies": { "dependencies": {
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1", "jsonfile": "^6.0.1",
"universalify": "^2.0.0" "universalify": "^2.0.0"
}, },
"engines": { "engines": {
"node": ">=12" "node": ">=14.14"
} }
}, },
"node_modules/fs.realpath": { "node_modules/fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
}, },
"node_modules/function-bind": { "node_modules/function-bind": {
"version": "1.1.1", "version": "1.1.1",
@ -3666,6 +3837,7 @@
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"dependencies": { "dependencies": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
@ -3887,6 +4059,7 @@
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"dependencies": { "dependencies": {
"once": "^1.3.0", "once": "^1.3.0",
"wrappy": "1" "wrappy": "1"
@ -3895,7 +4068,8 @@
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
}, },
"node_modules/ipaddr.js": { "node_modules/ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
@ -5697,6 +5871,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
}, },
@ -5971,6 +6146,7 @@
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true,
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
} }
@ -6380,6 +6556,7 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"dependencies": { "dependencies": {
"glob": "^7.1.3" "glob": "^7.1.3"
}, },
@ -6758,25 +6935,6 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true "dev": true
}, },
"node_modules/tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
"integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
"dependencies": {
"rimraf": "^3.0.0"
},
"engines": {
"node": ">=8.17.0"
}
},
"node_modules/tmp-promise": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
"integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
"dependencies": {
"tmp": "^0.2.0"
}
},
"node_modules/tmpl": { "node_modules/tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -6931,9 +7089,9 @@
} }
}, },
"node_modules/tsc-alias": { "node_modules/tsc-alias": {
"version": "1.8.1", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.1.tgz", "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.2.tgz",
"integrity": "sha512-d8Oc3y/XI9OvpdbdjzOWNYskEvFxqtobitHcTszmQr9iVt/tHL7pwV0WHKuCQ4uXHcHh+oDMx+uTlvMQBlpYcw==", "integrity": "sha512-ukBkcNekOgwtnSWYLD5QsMX3yQWg7JviAs8zg3qJGgu4LGtY3tsV4G6vnqvOXIDkbC+XL9vbhObWSpRA5/6wbg==",
"dev": true, "dev": true,
"dependencies": { "dependencies": {
"chokidar": "^3.5.3", "chokidar": "^3.5.3",
@ -7320,17 +7478,6 @@
} }
}, },
"dependencies": { "dependencies": {
"@actions/artifact": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-1.1.0.tgz",
"integrity": "sha512-shO+w/BAnzRnFhfsgUao8sxjByAMqDdfvek2LLKeCueBKXoTrAcp7U/hs9Fdx+z9g7Q0mbIrmHAzAAww4HK1bQ==",
"requires": {
"@actions/core": "^1.2.6",
"@actions/http-client": "^2.0.1",
"tmp": "^0.2.1",
"tmp-promise": "^3.0.2"
}
},
"@actions/core": { "@actions/core": {
"version": "1.10.0", "version": "1.10.0",
"resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz", "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.0.tgz",
@ -7340,23 +7487,6 @@
"uuid": "^8.3.2" "uuid": "^8.3.2"
} }
}, },
"@actions/exec": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@actions/exec/-/exec-1.1.1.tgz",
"integrity": "sha512-+sCcHHbVdk93a0XT19ECtO/gIXoxvdsgQLzb2fE2/5sIZmWQuluYyjPQtrtTHdU1YzTZ7bAPN4sITq2xi1679w==",
"requires": {
"@actions/io": "^1.0.1"
}
},
"@actions/glob": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@actions/glob/-/glob-0.3.0.tgz",
"integrity": "sha512-tJP1ZhF87fd6LBnaXWlahkyvdgvsLl7WnreW1EZaC8JWjpMXmzqWzQVe/IEYslrkT9ymibVrKyJN4UMD7uQM2w==",
"requires": {
"@actions/core": "^1.2.6",
"minimatch": "^3.0.4"
}
},
"@actions/http-client": { "@actions/http-client": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz", "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.0.1.tgz",
@ -7365,11 +7495,6 @@
"tunnel": "^0.0.6" "tunnel": "^0.0.6"
} }
}, },
"@actions/io": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@actions/io/-/io-1.1.2.tgz",
"integrity": "sha512-d+RwPlMp+2qmBfeLYPLXuSRykDIFEwdTA0MMxzS9kh4kvP1ftrc/9fzy6pX6qAjthdXruHQ6/6kjT/DNo5ALuw=="
},
"@ampproject/remapping": { "@ampproject/remapping": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz",
@ -8581,9 +8706,9 @@
} }
}, },
"@kie/mock-github": { "@kie/mock-github": {
"version": "0.1.1", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-0.1.1.tgz", "resolved": "https://registry.npmjs.org/@kie/mock-github/-/mock-github-0.1.2.tgz",
"integrity": "sha512-JO9a8ZcuMraTMHavzwIyGy2tl4+44cW3K5A42uTah8uM2HAitXMF47/q4cAFkMWKaL0pu3T8QEcZ+FdgpaTR+w==", "integrity": "sha512-WgJr0LmDye+YVZLmsRFVHlluLGVbRMosa1VKW7Nv1PvytcqHy1pdj8ljRN74uVHl7AcV0JDP/4Qb+wRwC4n18Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"@octokit/openapi-types-ghec": "^14.0.0", "@octokit/openapi-types-ghec": "^14.0.0",
@ -8594,6 +8719,19 @@
"nock": "^13.2.7", "nock": "^13.2.7",
"simple-git": "^3.8.0", "simple-git": "^3.8.0",
"totalist": "^3.0.0" "totalist": "^3.0.0"
},
"dependencies": {
"fs-extra": {
"version": "10.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
"dev": true,
"requires": {
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
}
}
} }
}, },
"@kwsites/file-exists": { "@kwsites/file-exists": {
@ -8906,9 +9044,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "17.0.45", "version": "18.11.17",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.17.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", "integrity": "sha512-HJSUJmni4BeDHhfzn6nF0sVmd1SMezP7/4F0Lq+aXzmp2xm9O7WXrUtHW/CHlYVtZUbByEvWidHqRtcJXGF2Ng==",
"dev": true "dev": true
}, },
"@types/prettier": { "@types/prettier": {
@ -8945,20 +9083,48 @@
"dev": true "dev": true
}, },
"@typescript-eslint/eslint-plugin": { "@typescript-eslint/eslint-plugin": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.47.0.tgz",
"integrity": "sha512-cOizjPlKEh0bXdFrBLTrI/J6B/QMlhwE9auOov53tgB+qMukH6/h8YAK/qw+QJGct/PTbdh2lytGyipxCcEtAw==", "integrity": "sha512-AHZtlXAMGkDmyLuLZsRpH3p4G/1iARIwc/T0vIem2YB+xW6pZaXYXzCBnZSF/5fdM97R9QqZWZ+h3iW10XgevQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/scope-manager": "5.45.1", "@typescript-eslint/scope-manager": "5.47.0",
"@typescript-eslint/type-utils": "5.45.1", "@typescript-eslint/type-utils": "5.47.0",
"@typescript-eslint/utils": "5.45.1", "@typescript-eslint/utils": "5.47.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"ignore": "^5.2.0", "ignore": "^5.2.0",
"natural-compare-lite": "^1.4.0", "natural-compare-lite": "^1.4.0",
"regexpp": "^3.2.0", "regexpp": "^3.2.0",
"semver": "^7.3.7", "semver": "^7.3.7",
"tsutils": "^3.21.0" "tsutils": "^3.21.0"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
"integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0"
}
},
"@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true
},
"@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
}
}
} }
}, },
"@typescript-eslint/parser": { "@typescript-eslint/parser": {
@ -8984,15 +9150,48 @@
} }
}, },
"@typescript-eslint/type-utils": { "@typescript-eslint/type-utils": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.47.0.tgz",
"integrity": "sha512-aosxFa+0CoYgYEl3aptLe1svP910DJq68nwEJzyQcrtRhC4BN0tJAvZGAe+D0tzjJmFXe+h4leSsiZhwBa2vrA==", "integrity": "sha512-1J+DFFrYoDUXQE1b7QjrNGARZE6uVhBqIvdaXTe5IN+NmEyD68qXR1qX1g2u4voA+nCaelQyG8w30SAOihhEYg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@typescript-eslint/typescript-estree": "5.45.1", "@typescript-eslint/typescript-estree": "5.47.0",
"@typescript-eslint/utils": "5.45.1", "@typescript-eslint/utils": "5.47.0",
"debug": "^4.3.4", "debug": "^4.3.4",
"tsutils": "^3.21.0" "tsutils": "^3.21.0"
},
"dependencies": {
"@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
"integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
}
}
} }
}, },
"@typescript-eslint/types": { "@typescript-eslint/types": {
@ -9017,19 +9216,62 @@
} }
}, },
"@typescript-eslint/utils": { "@typescript-eslint/utils": {
"version": "5.45.1", "version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.47.0.tgz",
"integrity": "sha512-rlbC5VZz68+yjAzQBc4I7KDYVzWG2X/OrqoZrMahYq3u8FFtmQYc+9rovo/7wlJH5kugJ+jQXV5pJMnofGmPRw==", "integrity": "sha512-U9xcc0N7xINrCdGVPwABjbAKqx4GK67xuMV87toI+HUqgXj26m6RBp9UshEXcTrgCkdGYFzgKLt8kxu49RilDw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/json-schema": "^7.0.9", "@types/json-schema": "^7.0.9",
"@types/semver": "^7.3.12", "@types/semver": "^7.3.12",
"@typescript-eslint/scope-manager": "5.45.1", "@typescript-eslint/scope-manager": "5.47.0",
"@typescript-eslint/types": "5.45.1", "@typescript-eslint/types": "5.47.0",
"@typescript-eslint/typescript-estree": "5.45.1", "@typescript-eslint/typescript-estree": "5.47.0",
"eslint-scope": "^5.1.1", "eslint-scope": "^5.1.1",
"eslint-utils": "^3.0.0", "eslint-utils": "^3.0.0",
"semver": "^7.3.7" "semver": "^7.3.7"
},
"dependencies": {
"@typescript-eslint/scope-manager": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.47.0.tgz",
"integrity": "sha512-dvJab4bFf7JVvjPuh3sfBUWsiD73aiftKBpWSfi3sUkysDQ4W8x+ZcFpNp7Kgv0weldhpmMOZBjx1wKN8uWvAw==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0"
}
},
"@typescript-eslint/types": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.47.0.tgz",
"integrity": "sha512-eslFG0Qy8wpGzDdYKu58CEr3WLkjwC5Usa6XbuV89ce/yN5RITLe1O8e+WFEuxnfftHiJImkkOBADj58ahRxSg==",
"dev": true
},
"@typescript-eslint/typescript-estree": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.47.0.tgz",
"integrity": "sha512-LxfKCG4bsRGq60Sqqu+34QT5qT2TEAHvSCCJ321uBWywgE2dS0LKcu5u+3sMGo+Vy9UmLOhdTw5JHzePV/1y4Q==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"@typescript-eslint/visitor-keys": "5.47.0",
"debug": "^4.3.4",
"globby": "^11.1.0",
"is-glob": "^4.0.3",
"semver": "^7.3.7",
"tsutils": "^3.21.0"
}
},
"@typescript-eslint/visitor-keys": {
"version": "5.47.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.47.0.tgz",
"integrity": "sha512-ByPi5iMa6QqDXe/GmT/hR6MZtVPi0SqMQPDx15FczCBXJo/7M8T88xReOALAfpBLm+zxpPfmhuEvPb577JRAEg==",
"dev": true,
"requires": {
"@typescript-eslint/types": "5.47.0",
"eslint-visitor-keys": "^3.3.0"
}
}
} }
}, },
"@typescript-eslint/visitor-keys": { "@typescript-eslint/visitor-keys": {
@ -9042,6 +9284,12 @@
"eslint-visitor-keys": "^3.3.0" "eslint-visitor-keys": "^3.3.0"
} }
}, },
"@vercel/ncc": {
"version": "0.36.0",
"resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.36.0.tgz",
"integrity": "sha512-/ZTUJ/ZkRt694k7KJNimgmHjtQcRuVwsST2Z6XfYveQIuBbHR+EqkTc1jfgPkQmMyk/vtpxo3nVxe8CNuau86A==",
"dev": true
},
"accepts": { "accepts": {
"version": "1.3.8", "version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@ -9222,7 +9470,8 @@
"balanced-match": { "balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
}, },
"before-after-hook": { "before-after-hook": {
"version": "2.2.3", "version": "2.2.3",
@ -9276,6 +9525,7 @@
"version": "1.1.11", "version": "1.1.11",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
"dev": true,
"requires": { "requires": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
"concat-map": "0.0.1" "concat-map": "0.0.1"
@ -9461,7 +9711,8 @@
"concat-map": { "concat-map": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"dev": true
}, },
"content-disposition": { "content-disposition": {
"version": "0.5.4", "version": "0.5.4",
@ -10075,9 +10326,9 @@
"dev": true "dev": true
}, },
"fs-extra": { "fs-extra": {
"version": "10.1.0", "version": "11.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.0.tgz",
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", "integrity": "sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw==",
"requires": { "requires": {
"graceful-fs": "^4.2.0", "graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1", "jsonfile": "^6.0.1",
@ -10087,7 +10338,8 @@
"fs.realpath": { "fs.realpath": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
"dev": true
}, },
"function-bind": { "function-bind": {
"version": "1.1.1", "version": "1.1.1",
@ -10134,6 +10386,7 @@
"version": "7.2.3", "version": "7.2.3",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"dev": true,
"requires": { "requires": {
"fs.realpath": "^1.0.0", "fs.realpath": "^1.0.0",
"inflight": "^1.0.4", "inflight": "^1.0.4",
@ -10283,6 +10536,7 @@
"version": "1.0.6", "version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
"dev": true,
"requires": { "requires": {
"once": "^1.3.0", "once": "^1.3.0",
"wrappy": "1" "wrappy": "1"
@ -10291,7 +10545,8 @@
"inherits": { "inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"dev": true
}, },
"ipaddr.js": { "ipaddr.js": {
"version": "1.9.1", "version": "1.9.1",
@ -11689,6 +11944,7 @@
"version": "3.1.2", "version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"requires": { "requires": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
} }
@ -11881,7 +12137,8 @@
"path-is-absolute": { "path-is-absolute": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==" "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
"dev": true
}, },
"path-key": { "path-key": {
"version": "3.1.1", "version": "3.1.1",
@ -12171,6 +12428,7 @@
"version": "3.0.2", "version": "3.0.2",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
"integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
"dev": true,
"requires": { "requires": {
"glob": "^7.1.3" "glob": "^7.1.3"
} }
@ -12448,22 +12706,6 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true "dev": true
}, },
"tmp": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
"integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
"requires": {
"rimraf": "^3.0.0"
}
},
"tmp-promise": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
"integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
"requires": {
"tmp": "^0.2.0"
}
},
"tmpl": { "tmpl": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz",
@ -12556,9 +12798,9 @@
} }
}, },
"tsc-alias": { "tsc-alias": {
"version": "1.8.1", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.1.tgz", "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.2.tgz",
"integrity": "sha512-d8Oc3y/XI9OvpdbdjzOWNYskEvFxqtobitHcTszmQr9iVt/tHL7pwV0WHKuCQ4uXHcHh+oDMx+uTlvMQBlpYcw==", "integrity": "sha512-ukBkcNekOgwtnSWYLD5QsMX3yQWg7JviAs8zg3qJGgu4LGtY3tsV4G6vnqvOXIDkbC+XL9vbhObWSpRA5/6wbg==",
"dev": true, "dev": true,
"requires": { "requires": {
"chokidar": "^3.5.3", "chokidar": "^3.5.3",

View file

@ -1,28 +1,32 @@
{ {
"name": "@lampajr/backporting", "name": "@lampajr/bper",
"version": "0.0.1", "version": "1.0.0",
"description": "Tool to execute automatic git backporting.", "description": "BPer is a tool to execute automatic git backporting.",
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"private": false, "private": false,
"main": "./build/src/bin/main.js", "main": "./dist/gha/index.js",
"bin": { "bin": {
"backporting": "./build/src/bin/main.js" "bper": "./dist/cli/index.js"
}, },
"files": [ "files": [
"build/" "dist/cli/index.js"
], ],
"scripts": { "scripts": {
"prepare": "husky install", "prepare": "husky install",
"clean": "rm -rf ./build", "clean": "rm -rf ./build ./dist",
"compile": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", "compile": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json",
"build": "npm run clean && npm run compile", "package": "npm run package:cli && npm run package:gha",
"package": "ncc build --source-map --license licenses.txt", "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", "test": "jest",
"test:report": "npm test -- --coverage --testResultsProcessor=jest-sonar-reporter", "test:report": "npm test -- --coverage --testResultsProcessor=jest-sonar-reporter",
"lint": "eslint . --ext .ts", "lint": "eslint . --ext .ts",
"lint:fix": "npm run lint -- --fix", "lint:fix": "npm run lint -- --fix",
"ts-node": "ts-node" "ts-node": "ts-node",
"preversion": "npm install && npm test",
"postversion": "git push && git push --tags"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -39,32 +43,31 @@
}, },
"homepage": "https://github.com/lampajr/backporting#readme", "homepage": "https://github.com/lampajr/backporting#readme",
"devDependencies": { "devDependencies": {
"@kie/mock-github": "^0.1.1", "@kie/mock-github": "^0.1.2",
"@types/fs-extra": "^9.0.13", "@types/fs-extra": "^9.0.13",
"@types/jest": "^28.1.8", "@types/jest": "^28.1.8",
"@types/node": "^17.0.41", "@types/node": "^18.11.17",
"@typescript-eslint/eslint-plugin": "^5.45.1", "@typescript-eslint/eslint-plugin": "^5.47.0",
"@typescript-eslint/parser": "^5.45.1", "@typescript-eslint/parser": "^5.45.1",
"@vercel/ncc": "^0.36.0",
"eslint": "^8.29.0", "eslint": "^8.29.0",
"husky": "^8.0.2", "husky": "^8.0.2",
"jest": "^29.3.1", "jest": "^29.3.1",
"jest-sonar-reporter": "^2.0.0", "jest-sonar-reporter": "^2.0.0",
"semver": "^7.3.8",
"ts-jest": "^29.0.3", "ts-jest": "^29.0.3",
"ts-node": "^10.8.1", "ts-node": "^10.8.1",
"tsc-alias": "^1.7.1", "tsc-alias": "^1.8.2",
"tsconfig-paths": "^4.1.0", "tsconfig-paths": "^4.1.0",
"typescript": "^4.9.3" "typescript": "^4.9.3"
}, },
"dependencies": { "dependencies": {
"@actions/artifact": "^1.1.0", "@actions/core": "^1.10.0",
"@actions/core": "^1.8.2",
"@actions/exec": "^1.1.1",
"@actions/glob": "^0.3.0",
"@octokit/rest": "^19.0.5", "@octokit/rest": "^19.0.5",
"@octokit/types": "^8.0.0", "@octokit/types": "^8.0.0",
"@octokit/webhooks-types": "^6.7.0", "@octokit/webhooks-types": "^6.7.0",
"commander": "^9.3.0", "commander": "^9.3.0",
"fs-extra": "^10.1.0", "fs-extra": "^11.1.0",
"simple-git": "^3.15.1" "simple-git": "^3.15.1"
} }
} }

12
src/bin/cli.ts Normal file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env node
import CLIArgsParser from "@bp/service/args/cli/cli-args-parser";
import Runner from "@bp/service/runner/runner";
// create CLI arguments parser
const parser = new CLIArgsParser();
// create runner
const runner = new Runner(parser);
runner.run();

12
src/bin/gha.ts Normal file
View file

@ -0,0 +1,12 @@
#!/usr/bin/env node
import GHAArgsParser from "@bp/service/args/gha/gha-args-parser";
import Runner from "@bp/service/runner/runner";
// create CLI arguments parser
const parser = new GHAArgsParser();
// create runner
const runner = new Runner(parser);
runner.run();

View file

@ -0,0 +1,10 @@
import { Args } from "@bp/service/args/args.types";
/**
* Abstract arguments parser interface in charge to parse inputs and
* produce a common Args object
*/
export default interface ArgsParser {
parse(): Args;
}

View file

@ -0,0 +1,11 @@
/**
* Input arguments
*/
export interface Args {
dryRun: boolean, // if enabled do not push anything remotely
auth: string, // git service auth, like github token
targetBranch: string, // branch on the target repo where the change should be backported to
pullRequest: string, // url of the pull request to backport
folder?: string, // local folder where the repositories should be cloned
author?: string, // backport pr author, default taken from pr
}

View file

@ -0,0 +1,49 @@
import ArgsParser from "@bp/service/args/args-parser";
import { Args } from "@bp/service/args/args.types";
import { Command } from "commander";
import { env } from "process";
interface PkgInfo {
name: string;
version: string;
description: string;
}
export default class CLIArgsParser implements ArgsParser {
private pkg: PkgInfo;
constructor() {
this.pkg = {
name: env.npm_package_name ?? "backporting",
version: env.npm_package_version ?? "0.0.0",
description: env.npm_package_description ?? ""
};
}
private getCommand(): Command {
return new Command(this.pkg.name)
.version(this.pkg.version)
.description(this.pkg.description)
.requiredOption("-tb, --target-branch <branch>", "branch where changes must be backported to.")
.requiredOption("-pr, --pull-request <pr url>", "pull request url, e.g., https://github.com/lampajr/backporting/pull/1.")
.option("-d, --dry-run", "if enabled the tool does not create any pull request nor push anything remotely", false)
.option("-a, --auth <auth>", "git service authentication string, e.g., github token.", "")
.option("-f, --folder <folder>", "local folder where the repo will be checked out, e.g., /tmp/folder.", undefined);
}
parse(): Args {
const opts = this.getCommand()
.parse()
.opts();
return {
dryRun: opts.dryRun,
auth: opts.auth,
pullRequest: opts.pullRequest,
targetBranch: opts.targetBranch,
folder: opts.folder
};
}
}

View file

@ -0,0 +1,17 @@
import ArgsParser from "@bp/service/args/args-parser";
import { Args } from "@bp/service/args/args.types";
import { getInput } from "@actions/core";
export default class GHAArgsParser implements ArgsParser {
parse(): Args {
return {
dryRun: getInput("dry-run") === "true",
auth: getInput("auth") ? getInput("auth") : "",
pullRequest: getInput("pull-request"),
targetBranch: getInput("target-branch"),
folder: getInput("folder") !== "" ? getInput("folder") : undefined
};
}
}

View file

@ -0,0 +1,22 @@
import { Args } from "@bp/service/args/args.types";
import { Configs } from "@bp/service/configs/configs.types";
/**
* Abstract configuration parser class in charge to parse
* Args and produces a common Configs object
*/
export default abstract class ConfigsParser {
abstract parse(args: Args): Promise<Configs>;
async parseAndValidate(args: Args): Promise<Configs> {
const configs: Configs = await this.parse(args);
// apply validation, throw errors if something is wrong
if (configs.originalPullRequest.state == "open" || !configs.originalPullRequest.merged) {
throw new Error("Provided pull request is not merged!");
}
return Promise.resolve(configs);
}
}

View file

@ -0,0 +1,17 @@
import { GitPullRequest } from "@bp/service/git/git.types";
/**
* Internal configuration object
*/
export interface Configs {
dryRun: boolean,
auth: string,
author: string, // author of the backport pr
folder: string,
targetBranch: string,
originalPullRequest: GitPullRequest,
backportPullRequest: GitPullRequest
}

View file

@ -0,0 +1,60 @@
import { Args } from "@bp/service/args/args.types";
import ConfigsParser from "@bp/service/configs/configs-parser";
import { Configs } from "@bp/service/configs/configs.types";
import GitService from "@bp/service/git/git-service";
import GitServiceFactory from "@bp/service/git/git-service-factory";
import { GitPullRequest } from "@bp/service/git/git.types";
export default class PullRequestConfigsParser extends ConfigsParser {
private gitService: GitService;
constructor() {
super();
this.gitService = GitServiceFactory.getService();
}
public async parse(args: Args): Promise<Configs> {
const pr: GitPullRequest = await this.gitService.getPullRequestFromUrl(args.pullRequest);
const folder: string = args.folder ?? this.getDefaultFolder();
return {
dryRun: args.dryRun,
auth: args.auth,
author: args.author ?? pr.author,
folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`,
targetBranch: args.targetBranch,
originalPullRequest: pr,
backportPullRequest: this.getDefaultBackportPullRequest(pr, args.targetBranch)
};
}
private getDefaultFolder() {
return "bp";
}
/**
* Create a default backport pull request starting from the target branch and
* the original pr to be backported
* @param originalPullRequest original pull request
* @param targetBranch target branch where the backport should be applied
* @returns {GitPullRequest}
*/
private getDefaultBackportPullRequest(originalPullRequest: GitPullRequest, targetBranch: string): GitPullRequest {
const reviewers = [];
reviewers.push(originalPullRequest.author);
if (originalPullRequest.mergedBy) {
reviewers.push(originalPullRequest.mergedBy);
}
return {
author: originalPullRequest.author,
title: `[${targetBranch}] ${originalPullRequest.title}`,
body: `**Backport:** ${originalPullRequest.htmlUrl}\r\n\r\n${originalPullRequest.body}\r\n\r\nPowered by [BPer](https://github.com/lampajr/backporting).`,
reviewers: [...new Set(reviewers)],
targetRepo: originalPullRequest.targetRepo,
sourceRepo: originalPullRequest.targetRepo,
commits: [] // TODO needed?
};
}
}

View file

@ -1,5 +1,5 @@
import LoggerService from "@gb/service/logger/logger-service"; import LoggerService from "@bp/service/logger/logger-service";
import LoggerServiceFactory from "@gb/service/logger/logger-service-factory"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import simpleGit, { SimpleGit } from "simple-git"; import simpleGit, { SimpleGit } from "simple-git";
import fs from "fs"; import fs from "fs";
@ -9,9 +9,13 @@ import fs from "fs";
export default class GitCLIService { export default class GitCLIService {
private readonly logger: LoggerService; private readonly logger: LoggerService;
private readonly auth: string;
private readonly author: string;
constructor() { constructor(auth: string, author: string) {
this.logger = LoggerServiceFactory.getLogger(); this.logger = LoggerServiceFactory.getLogger();
this.auth = auth;
this.author = author;
} }
/** /**
@ -22,7 +26,20 @@ export default class GitCLIService {
*/ */
private git(cwd?: string): SimpleGit { private git(cwd?: string): SimpleGit {
const gitConfig = { ...(cwd ? { baseDir: cwd } : {})}; const gitConfig = { ...(cwd ? { baseDir: cwd } : {})};
return simpleGit(gitConfig).addConfig("user.name", "Github").addConfig("user.email", "noreply@github.com"); return simpleGit(gitConfig).addConfig("user.name", this.author).addConfig("user.email", "noreply@github.com");
}
/**
* Update the provided remote URL by adding the auth token if not empty
* @param remoteURL remote link, e.g., https://github.com/lampajr/backporting-example.git
*/
private remoteWithAuth(remoteURL: string): string {
if (this.auth && this.author) {
return remoteURL.replace("://", `://${this.author}:${this.auth}@`);
}
// return remote as it is
return remoteURL;
} }
/** /**
@ -42,9 +59,9 @@ export default class GitCLIService {
* @param branch branch which should be cloned * @param branch branch which should be cloned
*/ */
async clone(from: string, to: string, branch: string): Promise<void> { async clone(from: string, to: string, branch: string): Promise<void> {
this.logger.info(`Cloning repository ${from}..`); this.logger.info(`Cloning repository ${from} to ${to}.`);
if (!fs.existsSync(to)) { if (!fs.existsSync(to)) {
await this.git().clone(from, to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]); await simpleGit().clone(this.remoteWithAuth(from), to, ["--quiet", "--shallow-submodules", "--no-tags", "--branch", branch]);
} else { } else {
this.logger.warn(`Folder ${to} already exist. Won't clone`); this.logger.warn(`Folder ${to} already exist. Won't clone`);
} }
@ -56,7 +73,7 @@ export default class GitCLIService {
* @param newBranch new branch name * @param newBranch new branch name
*/ */
async createLocalBranch(cwd: string, newBranch: string): Promise<void> { async createLocalBranch(cwd: string, newBranch: string): Promise<void> {
this.logger.info(`Creating branch ${newBranch}..`); this.logger.info(`Creating branch ${newBranch}.`);
await this.git(cwd).checkoutLocalBranch(newBranch); await this.git(cwd).checkoutLocalBranch(newBranch);
} }
@ -67,8 +84,8 @@ export default class GitCLIService {
* @param remoteName [optional] name of the remote, by default 'fork' is used * @param remoteName [optional] name of the remote, by default 'fork' is used
*/ */
async addRemote(cwd: string, remote: string, remoteName = "fork"): Promise<void> { async addRemote(cwd: string, remote: string, remoteName = "fork"): Promise<void> {
this.logger.info(`Adding new remote ${remote}..`); this.logger.info(`Adding new remote ${remote}.`);
await this.git(cwd).addRemote(remoteName, remote); await this.git(cwd).addRemote(remoteName, this.remoteWithAuth(remote));
} }
/** /**
@ -87,8 +104,8 @@ export default class GitCLIService {
* @param sha commit sha * @param sha commit sha
*/ */
async cherryPick(cwd: string, sha: string): Promise<void> { async cherryPick(cwd: string, sha: string): Promise<void> {
this.logger.info(`Cherry picking ${sha}..`); this.logger.info(`Cherry picking ${sha}.`);
await this.git(cwd).raw(["cherry-pick", "--strategy=recursive", "-X", "theirs", sha]); await this.git(cwd).raw(["cherry-pick", "-m", "1", "--strategy=recursive", "--strategy-option=theirs", sha]);
} }
/** /**
@ -98,7 +115,7 @@ export default class GitCLIService {
* @param remote [optional] remote to which the branch should be pushed to, by default 'origin' * @param remote [optional] remote to which the branch should be pushed to, by default 'origin'
*/ */
async push(cwd: string, branch: string, remote = "origin", force = false): Promise<void> { async push(cwd: string, branch: string, remote = "origin", force = false): Promise<void> {
this.logger.info(`Pushing ${branch} to ${remote}..`); this.logger.info(`Pushing ${branch} to ${remote}.`);
const options = ["--quiet"]; const options = ["--quiet"];
if (force) { if (force) {

View file

@ -1,12 +1,15 @@
import GitService from "@gb/service/git/git-service"; import GitService from "@bp/service/git/git-service";
import { GitServiceType } from "@gb/service/git/git.types"; import { GitServiceType } from "@bp/service/git/git.types";
import GitHubService from "@gb/service/git/github/github-service"; import GitHubService from "@bp/service/git/github/github-service";
import LoggerService from "@bp/service/logger/logger-service";
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
/** /**
* Singleton git service factory class * Singleton git service factory class
*/ */
export default class GitServiceFactory { export default class GitServiceFactory {
private static logger: LoggerService = LoggerServiceFactory.getLogger();
private static instance?: GitService; private static instance?: GitService;
public static getService(): GitService { public static getService(): GitService {
@ -25,7 +28,8 @@ export default class GitServiceFactory {
public static init(type: GitServiceType, auth: string): void { public static init(type: GitServiceType, auth: string): void {
if (GitServiceFactory.instance) { if (GitServiceFactory.instance) {
throw new Error("Git service already initialized!"); GitServiceFactory.logger.warn("Git service already initialized!");
return;
} }
switch(type) { switch(type) {

View file

@ -1,4 +1,4 @@
import { GitPullRequest } from "@gb/service/git/git.types"; import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
/** /**
* Git management service interface, which provides a common API for interacting * Git management service interface, which provides a common API for interacting
@ -17,17 +17,18 @@ import { GitPullRequest } from "@gb/service/git/git.types";
*/ */
getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest>; getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest>;
/**
* Get a pull request object from the underneath git service
* @param prUrl pull request html url
* @returns {Promise<PullRequest>}
*/
getPullRequestFromUrl(prUrl: string): Promise<GitPullRequest>;
// WRITE // WRITE
/** /**
* Create a new pull request on the underneath git service * Create a new pull request on the underneath git service
* @param owner repository's owner * @param backport backport pull request data
* @param repo repository's name
* @param head name of the source branch
* @param base name of the target branch
* @param title pr title
* @param body pr body
* @param reviewers pr list of reviewers
*/ */
createPullRequest(owner: string, repo: string, head: string, base: string, title: string, body: string, reviewers: string[]): Promise<void>; createPullRequest(backport: BackportPullRequest): Promise<void>;
} }

View file

@ -1,15 +1,34 @@
export interface GitPullRequest { export interface GitPullRequest {
url: string, author: string,
patchUrl: string, url?: string,
state: string, htmlUrl?: string,
state?: "open" | "closed",
merged?: boolean,
mergedBy?: string,
title: string, title: string,
body: string, body: string,
reviewers: string[], reviewers: string[],
targetRepo: string, targetRepo: GitRepository,
sourceRepo: string, sourceRepo: GitRepository,
commits: string[] commits: string[]
} }
export interface GitRepository {
owner: string,
project: string,
cloneUrl: string
}
export interface BackportPullRequest {
owner: string, // repository's owner
repo: string, // repository's name
head: string, // name of the source branch
base: string, // name of the target branch
title: string, // pr title
body: string, // pr body
reviewers: string[] // pr list of reviewers
}
export enum GitServiceType { export enum GitServiceType {
GITHUB = "github" GITHUB = "github"
} }

View file

@ -1,19 +1,30 @@
import { GitPullRequest } from "@gb/service/git/git.types"; import { GitPullRequest } from "@bp/service/git/git.types";
import { PullRequest, User } from "@octokit/webhooks-types"; import { PullRequest, User } from "@octokit/webhooks-types";
export default class GitHubMapper { export default class GitHubMapper {
mapPullRequest(pr: PullRequest): GitPullRequest { mapPullRequest(pr: PullRequest): GitPullRequest {
return { return {
author: pr.user.login,
url: pr.url, url: pr.url,
htmlUrl: pr.html_url,
title: pr.title, title: pr.title,
body: pr.body, body: pr.body ?? "",
patchUrl: pr.patch_url,
state: pr.state, state: pr.state,
merged: pr.merged ?? false,
mergedBy: pr.merged_by?.login,
reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => (r as User)?.login)), reviewers: pr.requested_reviewers.filter(r => "login" in r).map((r => (r as User)?.login)),
sourceRepo: pr.head.repo.full_name, sourceRepo: {
targetRepo: pr.base.repo.full_name, owner: pr.head.repo.full_name.split("/")[0],
commits: [pr.merge_commit_sha] project: pr.head.repo.full_name.split("/")[1],
} as GitPullRequest; cloneUrl: pr.head.repo.clone_url
},
targetRepo: {
owner: pr.base.repo.full_name.split("/")[0],
project: pr.base.repo.full_name.split("/")[1],
cloneUrl: pr.base.repo.clone_url
},
commits: [pr.merge_commit_sha as string]
};
} }
} }

View file

@ -1,16 +1,20 @@
import GitService from "@gb/service/git/git-service"; import GitService from "@bp/service/git/git-service";
import { GitPullRequest } from "@gb/service/git/git.types"; import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
import GitHubMapper from "@gb/service/git/github/github-mapper"; import GitHubMapper from "@bp/service/git/github/github-mapper";
import OctokitFactory from "@gb/service/git/github/octokit-factory"; import OctokitFactory from "@bp/service/git/github/octokit-factory";
import LoggerService from "@bp/service/logger/logger-service";
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
import { PullRequest } from "@octokit/webhooks-types"; import { PullRequest } from "@octokit/webhooks-types";
export default class GitHubService implements GitService { export default class GitHubService implements GitService {
private logger: LoggerService;
private octokit: Octokit; private octokit: Octokit;
private mapper: GitHubMapper; private mapper: GitHubMapper;
constructor(token: string) { constructor(token: string) {
this.logger = LoggerServiceFactory.getLogger();
this.octokit = OctokitFactory.getOctokit(token); this.octokit = OctokitFactory.getOctokit(token);
this.mapper = new GitHubMapper(); this.mapper = new GitHubMapper();
} }
@ -18,6 +22,7 @@ export default class GitHubService implements GitService {
// READ // READ
async getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest> { async getPullRequest(owner: string, repo: string, prNumber: number): Promise<GitPullRequest> {
this.logger.info(`Getting pull request ${owner}/${repo}/${prNumber}.`);
const { data } = await this.octokit.rest.pulls.get({ const { data } = await this.octokit.rest.pulls.get({
owner: owner, owner: owner,
repo: repo, repo: repo,
@ -27,12 +32,52 @@ export default class GitHubService implements GitService {
return this.mapper.mapPullRequest(data as PullRequest); return this.mapper.mapPullRequest(data as PullRequest);
} }
async getPullRequestFromUrl(prUrl: string): Promise<GitPullRequest> {
const {owner, project} = this.getRepositoryFromPrUrl(prUrl);
return this.getPullRequest(owner, project, parseInt(prUrl.substring(prUrl.lastIndexOf("/") + 1, prUrl.length)));
}
// WRITE // WRITE
// eslint-disable-next-line @typescript-eslint/no-unused-vars async createPullRequest(backport: BackportPullRequest): Promise<void> {
createPullRequest(owner: string, repo: string, head: string, base: string, title: string, body: string, reviewers: string[]): Promise<void> { this.logger.info(`Creating pull request ${backport.head} -> ${backport.base}.`);
// throw new Error("Method not implemented."); this.logger.info(`${JSON.stringify(backport, null, 2)}`);
// TODO implement
return Promise.resolve(); const { data } = await this.octokit.pulls.create({
owner: backport.owner,
repo: backport.repo,
head: backport.head,
base: backport.base,
title: backport.title,
body: backport.body
});
if (backport.reviewers.length > 0) {
try {
await this.octokit.pulls.requestReviewers({
owner: backport.owner,
repo: backport.repo,
pull_number: (data as PullRequest).number,
reviewers: backport.reviewers
});
} catch (error) {
this.logger.error(`Error requesting reviewers: ${error}`);
}
}
}
// UTILS
/**
* Extract repository owner and project from the pull request url
* @param prUrl pull request url
* @returns {{owner: string, project: string}}
*/
private getRepositoryFromPrUrl(prUrl: string): {owner: string, project: string} {
const elems: string[] = prUrl.split("/");
return {
owner: elems[elems.length - 4],
project: elems[elems.length - 3]
};
} }
} }

View file

@ -1,5 +1,5 @@
import LoggerService from "@gb/service/logger/logger-service"; import LoggerService from "@bp/service/logger/logger-service";
import LoggerServiceFactory from "@gb/service/logger/logger-service-factory"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { Octokit } from "@octokit/rest"; import { Octokit } from "@octokit/rest";
/** /**
@ -12,7 +12,7 @@ export default class OctokitFactory {
public static getOctokit(token: string): Octokit { public static getOctokit(token: string): Octokit {
if (!OctokitFactory.octokit) { if (!OctokitFactory.octokit) {
OctokitFactory.logger.info("Creating octokit instance.."); OctokitFactory.logger.info("Creating octokit instance.");
OctokitFactory.octokit = new Octokit({ OctokitFactory.octokit = new Octokit({
auth: token, auth: token,
userAgent: "lampajr/backporting" userAgent: "lampajr/backporting"

View file

@ -1,5 +1,5 @@
import Logger from "@gb/service/logger/logger"; import Logger from "@bp/service/logger/logger";
import LoggerService from "@gb/service/logger/logger-service"; import LoggerService from "@bp/service/logger/logger-service";
export default class ConsoleLoggerService implements LoggerService { export default class ConsoleLoggerService implements LoggerService {

View file

@ -1,5 +1,5 @@
import ConsoleLoggerService from "@gb/service/logger/console-logger-service"; import ConsoleLoggerService from "@bp/service/logger/console-logger-service";
import LoggerService from "@gb/service/logger/logger-service"; import LoggerService from "@bp/service/logger/logger-service";
/** /**
* Singleton factory class * Singleton factory class

View file

@ -0,0 +1,123 @@
import ArgsParser from "@bp/service/args/args-parser";
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 GitCLIService from "@bp/service/git/git-cli";
import GitService from "@bp/service/git/git-service";
import GitServiceFactory from "@bp/service/git/git-service-factory";
import { BackportPullRequest, GitPullRequest, GitServiceType } from "@bp/service/git/git.types";
import LoggerService from "@bp/service/logger/logger-service";
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
/**
* Main runner implementation, it implements the core logic flow
*/
export default class Runner {
private logger: LoggerService;
private argsParser: ArgsParser;
constructor(parser: ArgsParser) {
this.logger = LoggerServiceFactory.getLogger();
this.argsParser = parser;
}
/**
* Infer the remote GIT service to interact with based on the provided
* pull request URL
* @param prUrl provided pull request URL
* @returns {GitServiceType}
*/
private inferRemoteGitService(prUrl: string): GitServiceType {
const stdPrUrl = prUrl.toLowerCase().trim();
if (stdPrUrl.includes(GitServiceType.GITHUB.toString())) {
return GitServiceType.GITHUB;
}
throw new Error(`Remote GIT service not recognixed from PR url: ${prUrl}`);
}
/**
* Entry point invoked by the command line or gha
*/
async run(): Promise<void> {
this.logger.info("Starting process.");
try {
await this.execute();
this.logger.info("Process succeeded!");
process.exit(0);
} catch (error) {
this.logger.error(`${error}`);
this.logger.info("Process failed!");
process.exit(1);
}
}
/**
* Core logic
*/
async execute(): Promise<void>{
// 1. parse args
const args: Args = this.argsParser.parse();
if (args.dryRun) {
this.logger.warn("Dry run enabled!");
}
// 2. init git service
GitServiceFactory.init(this.inferRemoteGitService(args.pullRequest), args.auth);
const gitApi: GitService = GitServiceFactory.getService();
// 3. parse configs
const configs: Configs = await new PullRequestConfigsParser().parseAndValidate(args);
const originalPR: GitPullRequest = configs.originalPullRequest;
const backportPR: GitPullRequest = configs.backportPullRequest;
// start local git operations
const git: GitCLIService = new GitCLIService(configs.auth, configs.author);
// 4. clone the repository
await git.clone(configs.originalPullRequest.targetRepo.cloneUrl, configs.folder, configs.targetBranch);
// 5. create new branch from target one and checkout
const backportBranch = `bp-${configs.targetBranch}-${originalPR.commits.join("-")}`;
await git.createLocalBranch(configs.folder, backportBranch);
// 6. add new remote if source != target and fetch source repo
// Skip this, we assume the PR has been already merged
// 7. apply all changes to the new branch
for (const sha of originalPR.commits) {
await git.cherryPick(configs.folder, sha);
}
const backport: BackportPullRequest = {
owner: originalPR.targetRepo.owner,
repo: originalPR.targetRepo.project,
head: backportBranch,
base: configs.targetBranch,
title: backportPR.title,
body: backportPR.body,
reviewers: backportPR.reviewers
};
if (!configs.dryRun) {
// 8. push the new branch to origin
await git.push(configs.folder, backportBranch);
// 9. create pull request new branch -> target branch (using octokit)
await gitApi.createPullRequest(backport);
} else {
this.logger.warn("Pull request creation and remote push skipped!");
this.logger.info(`${JSON.stringify(backport, null, 2)}`);
}
}
}

View file

@ -0,0 +1,90 @@
import { Args } from "@bp/service/args/args.types";
import CLIArgsParser from "@bp/service/args/cli/cli-args-parser";
import { addProcessArgs, resetProcessArgs } from "../../../support/utils";
describe("cli args parser", () => {
let parser: CLIArgsParser;
beforeEach(() => {
// create a fresh new instance every time
parser = new CLIArgsParser();
// reset process.env variables
resetProcessArgs();
});
test("valid execution [default, short]", () => {
addProcessArgs([
"-tb",
"target",
"-pr",
"https://localhost/whatever/pulls/1"
]);
const args: Args = parser.parse();
expect(args.dryRun).toEqual(false);
expect(args.auth).toEqual("");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
test("valid execution [default, long]", () => {
addProcessArgs([
"--target-branch",
"target",
"--pull-request",
"https://localhost/whatever/pulls/1"
]);
const args: Args = parser.parse();
expect(args.dryRun).toEqual(false);
expect(args.auth).toEqual("");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
test("valid execution [override, short]", () => {
addProcessArgs([
"-d",
"-a",
"bearer-token",
"-tb",
"target",
"-pr",
"https://localhost/whatever/pulls/1"
]);
const args: Args = parser.parse();
expect(args.dryRun).toEqual(true);
expect(args.auth).toEqual("bearer-token");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
test("valid execution [override, long]", () => {
addProcessArgs([
"--dry-run",
"--auth",
"bearer-token",
"--target-branch",
"target",
"--pull-request",
"https://localhost/whatever/pulls/1"
]);
const args: Args = parser.parse();
expect(args.dryRun).toEqual(true);
expect(args.auth).toEqual("bearer-token");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
});

View file

@ -0,0 +1,50 @@
import { Args } from "@bp/service/args/args.types";
import GHAArgsParser from "@bp/service/args/gha/gha-args-parser";
import { spyGetInput } from "../../../support/utils";
describe("gha args parser", () => {
let parser: GHAArgsParser;
beforeEach(() => {
// create a fresh new instance every time
parser = new GHAArgsParser();
});
afterEach(() => {
jest.clearAllMocks();
});
test("valid execution [default]", () => {
spyGetInput({
"target-branch": "target",
"pull-request": "https://localhost/whatever/pulls/1"
});
const args: Args = parser.parse();
expect(args.dryRun).toEqual(false);
expect(args.auth).toEqual("");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
test("valid execution [override]", () => {
spyGetInput({
"dry-run": "true",
"auth": "bearer-token",
"target-branch": "target",
"pull-request": "https://localhost/whatever/pulls/1"
});
const args: Args = parser.parse();
expect(args.dryRun).toEqual(true);
expect(args.auth).toEqual("bearer-token");
expect(args.author).toEqual(undefined);
expect(args.folder).toEqual(undefined);
expect(args.targetBranch).toEqual("target");
expect(args.pullRequest).toEqual("https://localhost/whatever/pulls/1");
});
});

View file

@ -0,0 +1,132 @@
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 GitServiceFactory from "@bp/service/git/git-service-factory";
import { GitServiceType } from "@bp/service/git/git.types";
import { setupMoctokit } from "../../../support/moctokit/moctokit-support";
import { mergedPullRequestFixture, notMergedPullRequestFixture, repo, targetOwner } from "../../../support/moctokit/moctokit-data";
describe("pull request config parser", () => {
const mergedPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${mergedPullRequestFixture.number}`;
const notMergedPRUrl = `https://github.com/${targetOwner}/${repo}/pull/${notMergedPullRequestFixture.number}`;
let parser: PullRequestConfigsParser;
beforeAll(() => {
GitServiceFactory.init(GitServiceType.GITHUB, "whatever");
});
beforeEach(() => {
setupMoctokit();
parser = new PullRequestConfigsParser();
});
afterEach(() => {
jest.clearAllMocks();
});
test("parse configs from pull request [default]", async () => {
const args: Args = {
dryRun: false,
auth: "",
pullRequest: mergedPRUrl,
targetBranch: "prod"
};
const configs: Configs = await parser.parseAndValidate(args);
expect(configs.dryRun).toEqual(false);
expect(configs.author).toEqual("gh-user");
expect(configs.auth).toEqual("");
expect(configs.targetBranch).toEqual("prod");
expect(configs.folder).toEqual(process.cwd() + "/bp");
expect(configs.originalPullRequest).toEqual({
author: "gh-user",
url: "https://api.github.com/repos/owner/reponame/pulls/2368",
htmlUrl: "https://github.com/owner/reponame/pull/2368",
state: "closed",
merged: true,
mergedBy: "that-s-a-user",
title: "PR Title",
body: "Please review and merge",
reviewers: ["requested-gh-user", "gh-user"],
targetRepo: {
owner: "owner",
project: "reponame",
cloneUrl: "https://github.com/owner/reponame.git"
},
sourceRepo: {
owner: "fork",
project: "reponame",
cloneUrl: "https://github.com/fork/reponame.git"
},
commits: ["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]
});
expect(configs.backportPullRequest).toEqual({
author: "gh-user",
url: undefined,
htmlUrl: undefined,
title: "[prod] PR Title",
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge\r\n\r\nPowered by [BPer](https://github.com/lampajr/backporting).",
reviewers: ["gh-user", "that-s-a-user"],
targetRepo: {
owner: "owner",
project: "reponame",
cloneUrl: "https://github.com/owner/reponame.git"
},
sourceRepo: {
owner: "owner",
project: "reponame",
cloneUrl: "https://github.com/owner/reponame.git"
},
commits: []
});
});
test("override folder", async () => {
const args: Args = {
dryRun: true,
auth: "whatever",
pullRequest: mergedPRUrl,
targetBranch: "prod",
folder: "/tmp/test"
};
const configs: Configs = await parser.parseAndValidate(args);
expect(configs.dryRun).toEqual(true);
expect(configs.auth).toEqual("whatever");
expect(configs.targetBranch).toEqual("prod");
expect(configs.folder).toEqual("/tmp/test");
});
test("override author", async () => {
const args: Args = {
dryRun: true,
auth: "whatever",
pullRequest: mergedPRUrl,
targetBranch: "prod",
author: "another-user"
};
const configs: Configs = await parser.parseAndValidate(args);
expect(configs.dryRun).toEqual(true);
expect(configs.auth).toEqual("whatever");
expect(configs.targetBranch).toEqual("prod");
expect(configs.author).toEqual("another-user");
});
test("not merged pull request", async () => {
const args: Args = {
dryRun: true,
auth: "whatever",
pullRequest: notMergedPRUrl,
targetBranch: "prod"
};
expect(async () => await parser.parseAndValidate(args)).rejects.toThrow("Provided pull request is not merged!");
});
});

View file

@ -0,0 +1,114 @@
import GitCLIService from "@bp/service/git/git-cli";
import { FileState, GitActionTypes, MockGithub } from "@kie/mock-github";
import { spawnSync } from "child_process";
import { assert } from "console";
import path from "path";
import fs from "fs";
let git: GitCLIService;
let cwd: string;
let currentBranch: string;
let pushedBranches: string[];
let localBranches: string[];
let files: FileState[];
const mockGithub = new MockGithub(
{
repo: {
repoA: {
pushedBranches: ["sbranch", "tbranch"],
localBranches: ["lbranch"],
currentBranch: "main",
history: [
{
action: GitActionTypes.PUSH,
branch: "main",
},
{
action: GitActionTypes.PUSH,
branch: "sbranch",
},
{
action: GitActionTypes.PUSH,
branch: "tbranch",
},
],
},
},
},
path.join(__dirname, "setup-cli")
);
beforeAll(async () => {
//setup
await mockGithub.setup();
cwd = mockGithub.repo.getPath("repoA")!;
currentBranch = mockGithub.repo.getBranchState("repoA")!.currentBranch;
pushedBranches = mockGithub.repo.getBranchState("repoA")!.pushedBranches;
localBranches = mockGithub.repo.getBranchState("repoA")!.localBranches;
files = (await mockGithub.repo.getFileSystemState("repoA"))!;
//make sure the setup is correct to run this test suite
assert(
pushedBranches.length > 1,
"your configuration must have a repository with pushed branches other than main"
);
assert(
localBranches.length > 0,
"your configuration must have a repository with local branches i.e. not pushed branches"
);
assert(
files.length > 0,
"your configuration needs at least 1 file committed to some branch which is not the current branch"
);
});
afterAll(async () => {
await mockGithub.teardown();
});
beforeEach(() => {
// create a fresh instance of git before each test
git = new GitCLIService("", "author");
});
describe("git cli service", () => {
test("version", async () => {
const result = await git.version();
const actualVersion = spawnSync("git", ["version"]).stdout.toString();
const match = actualVersion.match(/(\d+\.\d+(\.\d+)?)/);
if (match) {
expect(result).toEqual(match[1]);
} else {
expect(result).toBe(undefined);
}
});
test("fetch", async () => {
await expect(git.fetch(cwd, currentBranch)).resolves.not.toThrowError();
});
test("local branch", async () => {
await expect(git.createLocalBranch(cwd, "new-local-branch")).resolves.not.toThrowError();
// use rev-parse to double check the current branch is the new one
const output = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], { cwd }).stdout.toString().trim();
expect(output).toEqual("new-local-branch");
});
test("push local branch", async () => {
const expressionToTest = "GIT_CHERRY_SHOULD_NOT_INCLUDE_THIS_MSG";
// create file to push
fs.writeFileSync(path.join(cwd, "test-push"), "testing git push");
// add and commit the file
spawnSync("git", ["add", "."], { cwd });
spawnSync("git", ["commit", "-m", expressionToTest], { cwd });
await git.push(cwd, currentBranch, "origin", false);
// use git cherry to verify this commit was pushed
const output = spawnSync("git", ["cherry", "-v"], { cwd }).stdout.toString();
expect(output.includes(expressionToTest)).toBe(false);
});
});

View file

@ -1,7 +1,7 @@
import GitServiceFactory from "@gb/service/git/git-service-factory"; import GitServiceFactory from "@bp/service/git/git-service-factory";
import { GitPullRequest, GitServiceType } from "@gb/service/git/git.types"; import { GitPullRequest, GitServiceType } from "@bp/service/git/git.types";
import GitHubService from "@gb/service/git/github/github-service"; import GitHubService from "@bp/service/git/github/github-service";
import { pullRequestNumber, repo, targetOwner } from "../../../support/moctokit/moctokit-data"; import { mergedPullRequestFixture, repo, targetOwner } from "../../../support/moctokit/moctokit-data";
import { setupMoctokit } from "../../../support/moctokit/moctokit-support"; import { setupMoctokit } from "../../../support/moctokit/moctokit-support";
describe("github service", () => { describe("github service", () => {
@ -21,9 +21,17 @@ describe("github service", () => {
}); });
test("get pull request: success", async () => { test("get pull request: success", async () => {
const res: GitPullRequest = await gitService.getPullRequest(targetOwner, repo, pullRequestNumber); const res: GitPullRequest = await gitService.getPullRequest(targetOwner, repo, mergedPullRequestFixture.number);
expect(res.sourceRepo).toBe("fork/reponame"); expect(res.sourceRepo).toEqual({
expect(res.targetRepo).toBe("owner/reponame"); owner: "fork",
project: "reponame",
cloneUrl: "https://github.com/fork/reponame.git"
});
expect(res.targetRepo).toEqual({
owner: "owner",
project: "reponame",
cloneUrl: "https://github.com/owner/reponame.git"
});
expect(res.title).toBe("PR Title"); expect(res.title).toBe("PR Title");
expect(res.commits.length).toBe(1); expect(res.commits.length).toBe(1);
expect(res.commits).toEqual(["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]); expect(res.commits).toEqual(["28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc"]);

View file

@ -0,0 +1,201 @@
import ArgsParser from "@bp/service/args/args-parser";
import Runner from "@bp/service/runner/runner";
import GitCLIService from "@bp/service/git/git-cli";
import GitHubService from "@bp/service/git/github/github-service";
import CLIArgsParser from "@bp/service/args/cli/cli-args-parser";
import { addProcessArgs, resetProcessArgs } from "../../support/utils";
import { setupMoctokit } from "../../support/moctokit/moctokit-support";
jest.mock("@bp/service/git/git-cli");
jest.spyOn(GitHubService.prototype, "createPullRequest");
let parser: ArgsParser;
let runner: Runner;
beforeEach(() => {
setupMoctokit();
// create CLI arguments parser
parser = new CLIArgsParser();
// create runner
runner = new Runner(parser);
});
afterEach(() => {
jest.clearAllMocks();
// reset process.env variables
resetProcessArgs();
});
describe("pull request runner test", () => {
test("with dry run", async () => {
addProcessArgs([
"-d",
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/2368"
]);
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(0);
});
test("overriding author", async () => {
addProcessArgs([
"-d",
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/2368"
]);
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(0);
});
test("with relative folder", async () => {
addProcessArgs([
"-d",
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/2368",
"-f",
"folder"
]);
await runner.execute();
const cwd = process.cwd() + "/folder";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(0);
});
test("with absolute folder", async () => {
addProcessArgs([
"-d",
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/2368",
"-f",
"/tmp/folder"
]);
await runner.execute();
const cwd = "/tmp/folder";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(0);
});
test("without dry run", async () => {
addProcessArgs([
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/2368"
]);
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(1);
expect(GitHubService.prototype.createPullRequest).toBeCalledWith({
owner: "owner",
repo: "reponame",
head: "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
base: "target",
title: "[target] PR Title",
body: expect.stringContaining("**Backport:** https://github.com/owner/reponame/pull/2368"),
reviewers: ["gh-user", "that-s-a-user"]
}
);
});
test("not merged pull request", async () => {
addProcessArgs([
"-tb",
"target",
"-pr",
"https://github.com/owner/reponame/pull/4444"
]);
expect(async () => await runner.execute()).rejects.toThrow("Provided pull request is not merged!");
});
});

View file

@ -0,0 +1,101 @@
import ArgsParser from "@bp/service/args/args-parser";
import Runner from "@bp/service/runner/runner";
import GitCLIService from "@bp/service/git/git-cli";
import GitHubService from "@bp/service/git/github/github-service";
import GHAArgsParser from "@bp/service/args/gha/gha-args-parser";
import { spyGetInput } from "../../support/utils";
import { setupMoctokit } from "../../support/moctokit/moctokit-support";
jest.mock("@bp/service/git/git-cli");
jest.spyOn(GitHubService.prototype, "createPullRequest");
let parser: ArgsParser;
let runner: Runner;
beforeEach(() => {
setupMoctokit();
// create GHA arguments parser
parser = new GHAArgsParser();
// create runner
runner = new Runner(parser);
});
afterEach(() => {
jest.clearAllMocks();
});
describe("pull request runner test", () => {
test("with dry run", async () => {
spyGetInput({
"dry-run": "true",
"target-branch": "target",
"pull-request": "https://github.com/owner/reponame/pull/2368"
});
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(0);
});
test("without dry run", async () => {
spyGetInput({
"target-branch": "target",
"pull-request": "https://github.com/owner/reponame/pull/2368"
});
await runner.execute();
const cwd = process.cwd() + "/bp";
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "target");
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.addRemote).toBeCalledTimes(0);
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc");
expect(GitHubService.prototype.createPullRequest).toBeCalledTimes(1);
expect(GitHubService.prototype.createPullRequest).toBeCalledWith({
owner: "owner",
repo: "reponame",
head: "bp-target-28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
base: "target",
title: "[target] PR Title",
body: expect.stringContaining("**Backport:** https://github.com/owner/reponame/pull/2368"),
reviewers: ["gh-user", "that-s-a-user"]
}
);
});
test("not merged pull request", async () => {
spyGetInput({
"target-branch": "target",
"pull-request": "https://github.com/owner/reponame/pull/4444"
});
expect(async () => await runner.execute()).rejects.toThrow("Provided pull request is not merged!");
});
});

View file

@ -1,11 +1,9 @@
export const targetOwner = "owner"; export const targetOwner = "owner";
export const sourceOwner = "fork"; export const sourceOwner = "fork";
export const repo = "reponame"; export const repo = "reponame";
export const pullRequestNumber = 2368; export const notFoundPullRequestNumber = 1;
export const invalidPullRequestNumber = 1;
export const commitRef = "91748965051fae1330ad58d15cf694e103267c87";
export const validPR = { export const mergedPullRequestFixture = {
"url": "https://api.github.com/repos/owner/reponame/pulls/2368", "url": "https://api.github.com/repos/owner/reponame/pulls/2368",
"id": 1137188271, "id": 1137188271,
"node_id": "PR_kwDOABTq6s5DyB2v", "node_id": "PR_kwDOABTq6s5DyB2v",
@ -18,22 +16,22 @@ export const validPR = {
"locked": false, "locked": false,
"title": "PR Title", "title": "PR Title",
"user": { "user": {
"login": "kie-ci", "login": "gh-user",
"id": 11995863, "id": 11995863,
"node_id": "MDQ6VXNlcjExOTk1ODYz", "node_id": "MDQ6VXNlcjExOTk1ODYz",
"avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/kie-ci", "url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/kie-ci", "html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/kie-ci/followers", "followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/kie-ci/following{/other_user}", "following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/kie-ci/gists{/gist_id}", "gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kie-ci/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kie-ci/subscriptions", "subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/kie-ci/orgs", "organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/kie-ci/repos", "repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/kie-ci/events{/privacy}", "events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/kie-ci/received_events", "received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
}, },
@ -49,22 +47,42 @@ export const validPR = {
], ],
"requested_reviewers": [ "requested_reviewers": [
{ {
"login": "ghuser", "login": "requested-gh-user",
"id": 1422582, "id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=", "node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/ghuser", "url": "https://api.github.com/users/requested-gh-user",
"html_url": "https://github.com/ghuser", "html_url": "https://github.com/requested-gh-user",
"followers_url": "https://api.github.com/users/ghuser/followers", "followers_url": "https://api.github.com/users/requested-gh-user/followers",
"following_url": "https://api.github.com/users/ghuser/following{/other_user}", "following_url": "https://api.github.com/users/requested-gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/ghuser/gists{/gist_id}", "gists_url": "https://api.github.com/users/requested-gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ghuser/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/requested-gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ghuser/subscriptions", "subscriptions_url": "https://api.github.com/users/requested-gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/ghuser/orgs", "organizations_url": "https://api.github.com/users/requested-gh-user/orgs",
"repos_url": "https://api.github.com/users/ghuser/repos", "repos_url": "https://api.github.com/users/requested-gh-user/repos",
"events_url": "https://api.github.com/users/ghuser/events{/privacy}", "events_url": "https://api.github.com/users/requested-gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/ghuser/received_events", "received_events_url": "https://api.github.com/users/requested-gh-user/received_events",
"type": "User",
"site_admin": false
},
{
"login": "gh-user",
"id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
} }
@ -109,7 +127,7 @@ export const validPR = {
"repo": { "repo": {
"id": 1370858, "id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4", "node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "optaplanner", "name": "reponame",
"full_name": "fork/reponame", "full_name": "fork/reponame",
"private": false, "private": false,
"owner": { "owner": {
@ -179,7 +197,7 @@ export const validPR = {
"ssh_url": "git@github.com:fork/reponame.git", "ssh_url": "git@github.com:fork/reponame.git",
"clone_url": "https://github.com/fork/reponame.git", "clone_url": "https://github.com/fork/reponame.git",
"svn_url": "https://github.com/fork/reponame", "svn_url": "https://github.com/fork/reponame",
"homepage": "https://www.optaplanner.org", "homepage": "https://www.reponame.org",
"size": 238339, "size": 238339,
"stargazers_count": 2811, "stargazers_count": 2811,
"watchers_count": 2811, "watchers_count": 2811,
@ -261,7 +279,7 @@ export const validPR = {
"repo": { "repo": {
"id": 1370858, "id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4", "node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "optaplanner", "name": "reponame",
"full_name": "owner/reponame", "full_name": "owner/reponame",
"private": false, "private": false,
"owner": { "owner": {
@ -331,7 +349,7 @@ export const validPR = {
"ssh_url": "git@github.com:owner/reponame.git", "ssh_url": "git@github.com:owner/reponame.git",
"clone_url": "https://github.com/owner/reponame.git", "clone_url": "https://github.com/owner/reponame.git",
"svn_url": "https://github.com/owner/reponame", "svn_url": "https://github.com/owner/reponame",
"homepage": "https://www.optaplanner.org", "homepage": "https://www.reponame.org",
"size": 238339, "size": 238339,
"stargazers_count": 2811, "stargazers_count": 2811,
"watchers_count": 2811, "watchers_count": 2811,
@ -420,22 +438,465 @@ export const validPR = {
"rebaseable": null, "rebaseable": null,
"mergeable_state": "unknown", "mergeable_state": "unknown",
"merged_by": { "merged_by": {
"login": "radtriste", "login": "that-s-a-user",
"id": 17157711, "id": 17157711,
"node_id": "MDQ6VXNlcjE3MTU3NzEx", "node_id": "MDQ6VXNlcjE3MTU3NzEx",
"avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4", "avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4",
"gravatar_id": "", "gravatar_id": "",
"url": "https://api.github.com/users/radtriste", "url": "https://api.github.com/users/that-s-a-user",
"html_url": "https://github.com/radtriste", "html_url": "https://github.com/that-s-a-user",
"followers_url": "https://api.github.com/users/radtriste/followers", "followers_url": "https://api.github.com/users/that-s-a-user/followers",
"following_url": "https://api.github.com/users/radtriste/following{/other_user}", "following_url": "https://api.github.com/users/that-s-a-user/following{/other_user}",
"gists_url": "https://api.github.com/users/radtriste/gists{/gist_id}", "gists_url": "https://api.github.com/users/that-s-a-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/radtriste/starred{/owner}{/repo}", "starred_url": "https://api.github.com/users/that-s-a-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/radtriste/subscriptions", "subscriptions_url": "https://api.github.com/users/that-s-a-user/subscriptions",
"organizations_url": "https://api.github.com/users/radtriste/orgs", "organizations_url": "https://api.github.com/users/that-s-a-user/orgs",
"repos_url": "https://api.github.com/users/radtriste/repos", "repos_url": "https://api.github.com/users/that-s-a-user/repos",
"events_url": "https://api.github.com/users/radtriste/events{/privacy}", "events_url": "https://api.github.com/users/that-s-a-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/radtriste/received_events", "received_events_url": "https://api.github.com/users/that-s-a-user/received_events",
"type": "User",
"site_admin": false
},
"comments": 0,
"review_comments": 0,
"maintainer_can_modify": false,
"commits": 2,
"additions": 2,
"deletions": 2,
"changed_files": 2
};
export const notMergedPullRequestFixture = {
"url": "https://api.github.com/repos/owner/reponame/pulls/4444",
"id": 1137188271,
"node_id": "PR_kwDOABTq6s5DyB2v",
"html_url": "https://github.com/owner/reponame/pull/4444",
"diff_url": "https://github.com/owner/reponame/pull/4444.diff",
"patch_url": "https://github.com/owner/reponame/pull/4444.patch",
"issue_url": "https://api.github.com/repos/owner/reponame/issues/4444",
"number": 4444,
"state": "closed",
"locked": false,
"title": "PR Title",
"user": {
"login": "gh-user",
"id": 11995863,
"node_id": "MDQ6VXNlcjExOTk1ODYz",
"avatar_url": "https://avatars.githubusercontent.com/u/11995863?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User",
"site_admin": false
},
"body": "Please review and merge",
"created_at": "2022-11-28T08:43:09Z",
"updated_at": "2022-11-28T10:11:53Z",
"closed_at": "2022-11-28T10:11:52Z",
"merged_at": "2022-11-28T10:11:52Z",
"merge_commit_sha": "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
"assignee": null,
"assignees": [
],
"requested_reviewers": [
{
"login": "gh-user",
"id": 1422582,
"node_id": "MDQ6VXNlcjE0MjI1ODI=",
"avatar_url": "https://avatars.githubusercontent.com/u/1422582?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/gh-user",
"html_url": "https://github.com/gh-user",
"followers_url": "https://api.github.com/users/gh-user/followers",
"following_url": "https://api.github.com/users/gh-user/following{/other_user}",
"gists_url": "https://api.github.com/users/gh-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/gh-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/gh-user/subscriptions",
"organizations_url": "https://api.github.com/users/gh-user/orgs",
"repos_url": "https://api.github.com/users/gh-user/repos",
"events_url": "https://api.github.com/users/gh-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/gh-user/received_events",
"type": "User",
"site_admin": false
}
],
"requested_teams": [
],
"labels": [
],
"milestone": null,
"draft": false,
"commits_url": "https://api.github.com/repos/owner/reponame/pulls/4444/commits",
"review_comments_url": "https://api.github.com/repos/owner/reponame/pulls/4444/comments",
"review_comment_url": "https://api.github.com/repos/owner/reponame/pulls/comments{/number}",
"comments_url": "https://api.github.com/repos/owner/reponame/issues/4444/comments",
"statuses_url": "https://api.github.com/repos/owner/reponame/statuses/91748965051fae1330ad58d15cf694e103267c87",
"head": {
"label": "kiegroup:bump-8.31.x-drools-8.31.0.Final",
"ref": "bump-8.31.x-drools-8.31.0.Final",
"sha": "91748965051fae1330ad58d15cf694e103267c87",
"user": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"repo": {
"id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "reponame",
"full_name": "fork/reponame",
"private": false,
"owner": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"html_url": "https://github.com/fork/reponame",
"description": "AI constraint solver in Java to optimize the vehicle routing problem, employee rostering, task assignment, maintenance scheduling, conference scheduling and other planning problems.",
"fork": false,
"url": "https://api.github.com/repos/fork/reponame",
"forks_url": "https://api.github.com/repos/fork/reponame/forks",
"keys_url": "https://api.github.com/repos/fork/reponame/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/fork/reponame/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/fork/reponame/teams",
"hooks_url": "https://api.github.com/repos/fork/reponame/hooks",
"issue_events_url": "https://api.github.com/repos/fork/reponame/issues/events{/number}",
"events_url": "https://api.github.com/repos/fork/reponame/events",
"assignees_url": "https://api.github.com/repos/fork/reponame/assignees{/user}",
"branches_url": "https://api.github.com/repos/fork/reponame/branches{/branch}",
"tags_url": "https://api.github.com/repos/fork/reponame/tags",
"blobs_url": "https://api.github.com/repos/fork/reponame/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/fork/reponame/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/fork/reponame/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/fork/reponame/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/fork/reponame/statuses/{sha}",
"languages_url": "https://api.github.com/repos/fork/reponame/languages",
"stargazers_url": "https://api.github.com/repos/fork/reponame/stargazers",
"contributors_url": "https://api.github.com/repos/fork/reponame/contributors",
"subscribers_url": "https://api.github.com/repos/fork/reponame/subscribers",
"subscription_url": "https://api.github.com/repos/fork/reponame/subscription",
"commits_url": "https://api.github.com/repos/fork/reponame/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/fork/reponame/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/fork/reponame/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/fork/reponame/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/fork/reponame/contents/{+path}",
"compare_url": "https://api.github.com/repos/fork/reponame/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/fork/reponame/merges",
"archive_url": "https://api.github.com/repos/fork/reponame/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/fork/reponame/downloads",
"issues_url": "https://api.github.com/repos/fork/reponame/issues{/number}",
"pulls_url": "https://api.github.com/repos/fork/reponame/pulls{/number}",
"milestones_url": "https://api.github.com/repos/fork/reponame/milestones{/number}",
"notifications_url": "https://api.github.com/repos/fork/reponame/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/fork/reponame/labels{/name}",
"releases_url": "https://api.github.com/repos/fork/reponame/releases{/id}",
"deployments_url": "https://api.github.com/repos/fork/reponame/deployments",
"created_at": "2011-02-15T19:38:23Z",
"updated_at": "2022-11-28T05:01:47Z",
"pushed_at": "2022-11-28T10:50:51Z",
"git_url": "git://github.com/fork/reponame.git",
"ssh_url": "git@github.com:fork/reponame.git",
"clone_url": "https://github.com/fork/reponame.git",
"svn_url": "https://github.com/fork/reponame",
"homepage": "https://www.reponame.org",
"size": 238339,
"stargazers_count": 2811,
"watchers_count": 2811,
"language": "Java",
"has_issues": false,
"has_projects": false,
"has_downloads": true,
"has_wiki": false,
"has_pages": false,
"has_discussions": false,
"forks_count": 878,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 30,
"license": {
"key": "apache-2.0",
"name": "Apache License 2.0",
"spdx_id": "Apache-2.0",
"url": "https://api.github.com/licenses/apache-2.0",
"node_id": "MDc6TGljZW5zZTI="
},
"allow_forking": true,
"is_template": false,
"web_commit_signoff_required": false,
"topics": [
"artificial-intelligence",
"branch-and-bound",
"constraint-programming",
"constraint-satisfaction-problem",
"constraint-solver",
"constraints",
"employee-rostering",
"java",
"local-search",
"mathematical-optimization",
"metaheuristics",
"optimization",
"rostering",
"scheduling",
"simulated-annealing",
"solver",
"tabu-search",
"traveling-salesman",
"traveling-salesman-problem",
"vehicle-routing-problem"
],
"visibility": "public",
"forks": 878,
"open_issues": 30,
"watchers": 2811,
"default_branch": "main"
}
},
"base": {
"label": "kiegroup:8.31.x",
"ref": "8.31.x",
"sha": "8cfc286765cb01c84a1d62c65519fa8032bfecbd",
"user": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"repo": {
"id": 1370858,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzcwODU4",
"name": "reponame",
"full_name": "owner/reponame",
"private": false,
"owner": {
"login": "kiegroup",
"id": 517980,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjUxNzk4MA==",
"avatar_url": "https://avatars.githubusercontent.com/u/517980?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kiegroup",
"html_url": "https://github.com/kiegroup",
"followers_url": "https://api.github.com/users/kiegroup/followers",
"following_url": "https://api.github.com/users/kiegroup/following{/other_user}",
"gists_url": "https://api.github.com/users/kiegroup/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kiegroup/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kiegroup/subscriptions",
"organizations_url": "https://api.github.com/users/kiegroup/orgs",
"repos_url": "https://api.github.com/users/kiegroup/repos",
"events_url": "https://api.github.com/users/kiegroup/events{/privacy}",
"received_events_url": "https://api.github.com/users/kiegroup/received_events",
"type": "Organization",
"site_admin": false
},
"html_url": "https://github.com/owner/reponame",
"description": "AI constraint solver in Java to optimize the vehicle routing problem, employee rostering, task assignment, maintenance scheduling, conference scheduling and other planning problems.",
"fork": false,
"url": "https://api.github.com/repos/owner/reponame",
"forks_url": "https://api.github.com/repos/owner/reponame/forks",
"keys_url": "https://api.github.com/repos/owner/reponame/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/owner/reponame/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/owner/reponame/teams",
"hooks_url": "https://api.github.com/repos/owner/reponame/hooks",
"issue_events_url": "https://api.github.com/repos/owner/reponame/issues/events{/number}",
"events_url": "https://api.github.com/repos/owner/reponame/events",
"assignees_url": "https://api.github.com/repos/owner/reponame/assignees{/user}",
"branches_url": "https://api.github.com/repos/owner/reponame/branches{/branch}",
"tags_url": "https://api.github.com/repos/owner/reponame/tags",
"blobs_url": "https://api.github.com/repos/owner/reponame/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/owner/reponame/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/owner/reponame/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/owner/reponame/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/owner/reponame/statuses/{sha}",
"languages_url": "https://api.github.com/repos/owner/reponame/languages",
"stargazers_url": "https://api.github.com/repos/owner/reponame/stargazers",
"contributors_url": "https://api.github.com/repos/owner/reponame/contributors",
"subscribers_url": "https://api.github.com/repos/owner/reponame/subscribers",
"subscription_url": "https://api.github.com/repos/owner/reponame/subscription",
"commits_url": "https://api.github.com/repos/owner/reponame/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/owner/reponame/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/owner/reponame/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/owner/reponame/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/owner/reponame/contents/{+path}",
"compare_url": "https://api.github.com/repos/owner/reponame/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/owner/reponame/merges",
"archive_url": "https://api.github.com/repos/owner/reponame/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/owner/reponame/downloads",
"issues_url": "https://api.github.com/repos/owner/reponame/issues{/number}",
"pulls_url": "https://api.github.com/repos/owner/reponame/pulls{/number}",
"milestones_url": "https://api.github.com/repos/owner/reponame/milestones{/number}",
"notifications_url": "https://api.github.com/repos/owner/reponame/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/owner/reponame/labels{/name}",
"releases_url": "https://api.github.com/repos/owner/reponame/releases{/id}",
"deployments_url": "https://api.github.com/repos/owner/reponame/deployments",
"created_at": "2011-02-15T19:38:23Z",
"updated_at": "2022-11-28T05:01:47Z",
"pushed_at": "2022-11-28T10:50:51Z",
"git_url": "git://github.com/owner/reponame.git",
"ssh_url": "git@github.com:owner/reponame.git",
"clone_url": "https://github.com/owner/reponame.git",
"svn_url": "https://github.com/owner/reponame",
"homepage": "https://www.reponame.org",
"size": 238339,
"stargazers_count": 2811,
"watchers_count": 2811,
"language": "Java",
"has_issues": false,
"has_projects": false,
"has_downloads": true,
"has_wiki": false,
"has_pages": false,
"has_discussions": false,
"forks_count": 878,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 30,
"license": {
"key": "apache-2.0",
"name": "Apache License 2.0",
"spdx_id": "Apache-2.0",
"url": "https://api.github.com/licenses/apache-2.0",
"node_id": "MDc6TGljZW5zZTI="
},
"allow_forking": true,
"is_template": false,
"web_commit_signoff_required": false,
"topics": [
"artificial-intelligence",
"branch-and-bound",
"constraint-programming",
"constraint-satisfaction-problem",
"constraint-solver",
"constraints",
"employee-rostering",
"java",
"local-search",
"mathematical-optimization",
"metaheuristics",
"optimization",
"rostering",
"scheduling",
"simulated-annealing",
"solver",
"tabu-search",
"traveling-salesman",
"traveling-salesman-problem",
"vehicle-routing-problem"
],
"visibility": "public",
"forks": 878,
"open_issues": 30,
"watchers": 2811,
"default_branch": "main"
}
},
"_links": {
"self": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444"
},
"html": {
"href": "https://github.com/owner/reponame/pull/4444"
},
"issue": {
"href": "https://api.github.com/repos/owner/reponame/issues/4444"
},
"comments": {
"href": "https://api.github.com/repos/owner/reponame/issues/4444/comments"
},
"review_comments": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444/comments"
},
"review_comment": {
"href": "https://api.github.com/repos/owner/reponame/pulls/comments{/number}"
},
"commits": {
"href": "https://api.github.com/repos/owner/reponame/pulls/4444/commits"
},
"statuses": {
"href": "https://api.github.com/repos/owner/reponame/statuses/91748965051fae1330ad58d15cf694e103267c87"
}
},
"author_association": "CONTRIBUTOR",
"auto_merge": null,
"active_lock_reason": null,
"merged": null,
"mergeable": null,
"rebaseable": null,
"mergeable_state": "unknown",
"merged_by": {
"login": "that-s-a-user",
"id": 17157711,
"node_id": "MDQ6VXNlcjE3MTU3NzEx",
"avatar_url": "https://avatars.githubusercontent.com/u/17157711?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/that-s-a-user",
"html_url": "https://github.com/that-s-a-user",
"followers_url": "https://api.github.com/users/that-s-a-user/followers",
"following_url": "https://api.github.com/users/that-s-a-user/following{/other_user}",
"gists_url": "https://api.github.com/users/that-s-a-user/gists{/gist_id}",
"starred_url": "https://api.github.com/users/that-s-a-user/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/that-s-a-user/subscriptions",
"organizations_url": "https://api.github.com/users/that-s-a-user/orgs",
"repos_url": "https://api.github.com/users/that-s-a-user/repos",
"events_url": "https://api.github.com/users/that-s-a-user/events{/privacy}",
"received_events_url": "https://api.github.com/users/that-s-a-user/received_events",
"type": "User", "type": "User",
"site_admin": false "site_admin": false
}, },

View file

@ -1,11 +1,11 @@
import LoggerServiceFactory from "@gb/service/logger/logger-service-factory"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
import { Moctokit } from "@kie/mock-github"; import { Moctokit } from "@kie/mock-github";
import { targetOwner, repo, pullRequestNumber, validPR, invalidPullRequestNumber } from "./moctokit-data"; import { targetOwner, repo, mergedPullRequestFixture, notMergedPullRequestFixture, notFoundPullRequestNumber } from "./moctokit-data";
const logger = LoggerServiceFactory.getLogger(); const logger = LoggerServiceFactory.getLogger();
export const setupMoctokit = (): Moctokit => { export const setupMoctokit = (): Moctokit => {
logger.debug("Setting up moctokit.."); logger.debug("Setting up moctokit.");
const mock = new Moctokit(); const mock = new Moctokit();
@ -16,18 +16,36 @@ export const setupMoctokit = (): Moctokit => {
.get({ .get({
owner: targetOwner, owner: targetOwner,
repo: repo, repo: repo,
pull_number: pullRequestNumber pull_number: mergedPullRequestFixture.number
}) })
.reply({ .reply({
status: 200, status: 200,
data: validPR data: mergedPullRequestFixture
});
mock.rest.pulls
.get({
owner: targetOwner,
repo: repo,
pull_number: notMergedPullRequestFixture.number
})
.reply({
status: 200,
data: notMergedPullRequestFixture
}); });
mock.rest.pulls mock.rest.pulls
.create() .create()
.reply({ .reply({
status: 201, status: 201,
data: validPR data: mergedPullRequestFixture
});
mock.rest.pulls
.requestReviewers()
.reply({
status: 201,
data: mergedPullRequestFixture
}); });
@ -36,7 +54,7 @@ export const setupMoctokit = (): Moctokit => {
.get({ .get({
owner: targetOwner, owner: targetOwner,
repo: repo, repo: repo,
pull_number: invalidPullRequestNumber pull_number: notFoundPullRequestNumber
}) })
.reply({ .reply({
status: 404, status: 404,

17
test/support/utils.ts Normal file
View file

@ -0,0 +1,17 @@
import * as core from "@actions/core";
export const addProcessArgs = (args: string[]) => {
process.argv = [...process.argv, ...args];
};
export const resetProcessArgs = () => {
process.argv = ["node", "backporting"];
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const spyGetInput = (obj: any) => {
const mock = jest.spyOn(core, "getInput");
mock.mockImplementation((name: string) : string => {
return obj[name];
});
};

View file

@ -33,7 +33,7 @@
"baseUrl": "./", "baseUrl": "./",
/* Specify the base directory to resolve non-relative module names. */ /* Specify the base directory to resolve non-relative module names. */
"paths": { "paths": {
"@gb/*": [ "@bp/*": [
"./src/*" "./src/*"
] ]
}, },