mirror of
https://github.com/kiegroup/git-backporting.git
synced 2025-04-21 19:18:43 +00:00
Compare commits
No commits in common. "main" and "v4.7.1" have entirely different histories.
51 changed files with 6621 additions and 10983 deletions
4
.github/workflows/ci.yml
vendored
4
.github/workflows/ci.yml
vendored
|
@ -19,9 +19,9 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Node ${{ matrix.node-version }}
|
- name: Setup Node ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
2
.github/workflows/coverage.yml
vendored
2
.github/workflows/coverage.yml
vendored
|
@ -16,7 +16,7 @@ jobs:
|
||||||
coverage:
|
coverage:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: ArtiomTr/jest-coverage-report-action@v2
|
- uses: ArtiomTr/jest-coverage-report-action@v2
|
||||||
with:
|
with:
|
||||||
test-script: npm test
|
test-script: npm test
|
4
.github/workflows/prepare-release.yml
vendored
4
.github/workflows/prepare-release.yml
vendored
|
@ -19,11 +19,11 @@ jobs:
|
||||||
name: Prepare release
|
name: Prepare release
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- name: Git config
|
- name: Git config
|
||||||
|
|
4
.github/workflows/pull-request.yml
vendored
4
.github/workflows/pull-request.yml
vendored
|
@ -24,9 +24,9 @@ jobs:
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Setup Node ${{ matrix.node-version }}
|
- name: Setup Node ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
|
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
|
@ -17,11 +17,11 @@ jobs:
|
||||||
name: Release package
|
name: Release package
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 18
|
||||||
- name: Git config
|
- name: Git config
|
||||||
|
|
43
CHANGELOG.md
43
CHANGELOG.md
|
@ -1,48 +1,5 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## <small>4.8.5 (2025-04-15)</small>
|
|
||||||
|
|
||||||
* build(deps): audit fix (#150) ([3a9d367](https://github.com/kiegroup/git-backporting/commit/3a9d367)), closes [#150](https://github.com/kiegroup/git-backporting/issues/150)
|
|
||||||
* build(deps): upgrade release-it to v18 (#153) ([c9a7375](https://github.com/kiegroup/git-backporting/commit/c9a7375)), closes [#153](https://github.com/kiegroup/git-backporting/issues/153)
|
|
||||||
* fix(#151): fix gitlab post comments url (#152) ([d74a787](https://github.com/kiegroup/git-backporting/commit/d74a787)), closes [#152](https://github.com/kiegroup/git-backporting/issues/152)
|
|
||||||
|
|
||||||
## [4.8.4](https://github.com/kiegroup/git-backporting/compare/v4.8.3...v4.8.4) (2024-11-02)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* abort conflicting cherry-pick before starting new one ([#146](https://github.com/kiegroup/git-backporting/issues/146)) ([3deee59](https://github.com/kiegroup/git-backporting/commit/3deee59d4c3b726ae131b5974af4618dd5e7d1d2))
|
|
||||||
|
|
||||||
## [4.8.3](https://github.com/kiegroup/git-backporting/compare/v4.8.2...v4.8.3) (2024-10-10)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* auto-no-squash inference for GitLab ([#140](https://github.com/kiegroup/git-backporting/issues/140)) ([b4d0481](https://github.com/kiegroup/git-backporting/commit/b4d0481c5649115367f1ae0724d12d55b6b86e10))
|
|
||||||
|
|
||||||
## [4.8.2](https://github.com/kiegroup/git-backporting/compare/v4.8.1...v4.8.2) (2024-10-07)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* cherry-pick order on GitLab by reversing commmit list only once ([#137](https://github.com/kiegroup/git-backporting/issues/137)) ([e2d73d0](https://github.com/kiegroup/git-backporting/commit/e2d73d050c8387c0858877ac3c56c565bacaf4f9))
|
|
||||||
* handle Codeberg returning null entry in requested_reviewers ([#136](https://github.com/kiegroup/git-backporting/issues/136)) ([1e8358b](https://github.com/kiegroup/git-backporting/commit/1e8358bb2c461c56cf86e82bec4d71284866b13b))
|
|
||||||
|
|
||||||
## [4.8.1](https://github.com/kiegroup/git-backporting/compare/v4.8.0...v4.8.1) (2024-07-16)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **gh130:** apply commits in the correct order on github ([#131](https://github.com/kiegroup/git-backporting/issues/131)) ([cb3473d](https://github.com/kiegroup/git-backporting/commit/cb3473d7c9de66cb7bec188f08538e134cdc4bc0))
|
|
||||||
|
|
||||||
## [4.8.0](https://github.com/kiegroup/git-backporting/compare/v4.7.1...v4.8.0) (2024-04-11)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* auto-detect the value of the no-squash option ([#118](https://github.com/kiegroup/git-backporting/issues/118)) ([6042bcc](https://github.com/kiegroup/git-backporting/commit/6042bcc40ba83593a23dfe4d92cf50655a05b1cd))
|
|
||||||
* implement error notification as pr comment ([#124](https://github.com/kiegroup/git-backporting/issues/124)) ([2bb7f73](https://github.com/kiegroup/git-backporting/commit/2bb7f731127e099d1f196e6785e992589f7c4940))
|
|
||||||
|
|
||||||
## [4.7.1](https://github.com/kiegroup/git-backporting/compare/v4.7.0...v4.7.1) (2024-04-03)
|
## [4.7.1](https://github.com/kiegroup/git-backporting/compare/v4.7.0...v4.7.1) (2024-04-03)
|
||||||
|
|
||||||
|
|
||||||
|
|
12
README.md
12
README.md
|
@ -70,13 +70,7 @@ It works in this way: given the provided `pull/merge request` it infers the serv
|
||||||
|
|
||||||
After that it clones the corresponding git repository, check out in the provided `target branch` and create a new branch from that (name automatically generated if not provided as option).
|
After that it clones the corresponding git repository, check out in the provided `target branch` and create a new branch from that (name automatically generated if not provided as option).
|
||||||
|
|
||||||
By default the tool will try to cherry-pick the single squashed/merged commit into the newly created branch. The `--no-squash` and `--auto-no-squash` options control this behavior according the following table.
|
By default the tool will try to cherry-pick the single squashed/merged commit into the newly created branch (please consider using `--no-squash` option if you want to cherry-pick all commits belonging to the provided pull request).
|
||||||
|
|
||||||
| No squash | Auto no squash |Behavior|
|
|
||||||
|---|---|---|
|
|
||||||
| unset/false | unset/false | cherry-pick a single commit, squashed or merged |
|
|
||||||
| set/true | unset/false | cherry-pick all commits found in the the original pull/merge request|
|
|
||||||
| (ignored) | set/true | cherry-pick all commits if the original pull/merge request was merged, a single commit if it was squashed |
|
|
||||||
|
|
||||||
Based on the original pull request, creates a new one containing the backporting to the target branch. Note that most of these information can be overridden with appropriate CLI options or GHA inputs.
|
Based on the original pull request, creates a new one containing the backporting to the target branch. Note that most of these information can be overridden with appropriate CLI options or GHA inputs.
|
||||||
|
|
||||||
|
@ -127,13 +121,11 @@ This tool comes with some inputs that allow users to override the default behavi
|
||||||
| Backport Branch Names | --bp-branch-name | N | Comma separated lists of the backporting pull request branch names, if they exceeds 250 chars they will be truncated | bp-{target-branch}-{sha1}...{shaN} |
|
| Backport Branch Names | --bp-branch-name | N | Comma separated lists of the backporting pull request branch names, if they exceeds 250 chars they will be truncated | bp-{target-branch}-{sha1}...{shaN} |
|
||||||
| Labels | --labels | N | Provide custom labels to be added to the backporting pull request | [] |
|
| Labels | --labels | N | Provide custom labels to be added to the backporting pull request | [] |
|
||||||
| Inherit labels | --inherit-labels | N | If enabled inherit lables from the original pull request | false |
|
| Inherit labels | --inherit-labels | N | If enabled inherit lables from the original pull request | false |
|
||||||
| No squash | --no-squash | N | Backport all commits found in the pull request. The default behavior is to only backport the first commit that was merged in the base branch. | |
|
| No squash | --no-squash | N | If provided the backporting will try to backport all pull request commits without squashing | false |
|
||||||
| Auto no squash | --auto-no-squash | N | If the pull request was merged or is open, backport all commits. If the pull request commits were squashed, backport the squashed commit. | |
|
|
||||||
| Strategy | --strategy | N | Cherry pick merging strategy, see [git-merge](https://git-scm.com/docs/git-merge#_merge_strategies) doc for all possible values | "recursive" |
|
| Strategy | --strategy | N | Cherry pick merging strategy, see [git-merge](https://git-scm.com/docs/git-merge#_merge_strategies) doc for all possible values | "recursive" |
|
||||||
| Strategy Option | --strategy-option | N | Cherry pick merging strategy option, see [git-merge](https://git-scm.com/docs/git-merge#_merge_strategies) doc for all possible values | "theirs" |
|
| Strategy Option | --strategy-option | N | Cherry pick merging strategy option, see [git-merge](https://git-scm.com/docs/git-merge#_merge_strategies) doc for all possible values | "theirs" |
|
||||||
| Cherry-pick Options | --cherry-pick-options | N | Additional cherry-pick options, see [git-cherry-pick](https://git-scm.com/docs/git-cherry-pick) doc for all possible values | "theirs" |
|
| Cherry-pick Options | --cherry-pick-options | N | Additional cherry-pick options, see [git-cherry-pick](https://git-scm.com/docs/git-cherry-pick) doc for all possible values | "theirs" |
|
||||||
| Additional comments | --comments | N | Semicolon separated list of additional comments to be posted to the backported pull request | [] |
|
| Additional comments | --comments | N | Semicolon separated list of additional comments to be posted to the backported pull request | [] |
|
||||||
| Enable error notification | --enable-err-notification | N | If true, enable the error notification as comment on the original pull request | false |
|
|
||||||
| Dry Run | -d, --dry-run | N | If enabled the tool does not push nor create anything remotely, use this to skip PR creation | false |
|
| Dry Run | -d, --dry-run | N | If enabled the tool does not push nor create anything remotely, use this to skip PR creation | false |
|
||||||
|
|
||||||
> **NOTE**: `pull request` and (`target branch` or `target branch pattern`) are *mandatory*, they must be provided as CLI options or as part of the configuration file (if used).
|
> **NOTE**: `pull request` and (`target branch` or `target branch pattern`) are *mandatory*, they must be provided as CLI options or as part of the configuration file (if used).
|
||||||
|
|
15
action.yml
15
action.yml
|
@ -85,14 +85,10 @@ inputs:
|
||||||
default: "false"
|
default: "false"
|
||||||
no-squash:
|
no-squash:
|
||||||
description: >
|
description: >
|
||||||
Backport all commits found in the pull request.
|
If set to true the tool will backport all commits as part of the pull request
|
||||||
The default behavior is to only backport the first commit that was merged in the base branch.
|
instead of the suqashed one
|
||||||
required: false
|
|
||||||
auto-no-squash:
|
|
||||||
description: >
|
|
||||||
If the pull request was merged or is open, backport all commits.
|
|
||||||
If the pull request commits were squashed, backport the squashed commit.
|
|
||||||
required: false
|
required: false
|
||||||
|
default: "false"
|
||||||
strategy:
|
strategy:
|
||||||
description: Cherry-pick merge strategy
|
description: Cherry-pick merge strategy
|
||||||
required: false
|
required: false
|
||||||
|
@ -109,11 +105,6 @@ inputs:
|
||||||
description: >
|
description: >
|
||||||
Semicolon separated list of additional comments to be posted to the backported pull request
|
Semicolon separated list of additional comments to be posted to the backported pull request
|
||||||
required: false
|
required: false
|
||||||
enable-err-notification:
|
|
||||||
description: >
|
|
||||||
If true, enable the error notification as comment on the original pull request
|
|
||||||
required: false
|
|
||||||
default: "false"
|
|
||||||
|
|
||||||
runs:
|
runs:
|
||||||
using: node20
|
using: node20
|
||||||
|
|
1930
dist/cli/index.js
vendored
1930
dist/cli/index.js
vendored
File diff suppressed because it is too large
Load diff
1924
dist/gha/index.js
vendored
1924
dist/gha/index.js
vendored
File diff suppressed because it is too large
Load diff
|
@ -35,7 +35,7 @@ jobs:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v3
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Backporting
|
- name: Backporting
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
[tools]
|
|
||||||
node = "20"
|
|
9203
package-lock.json
generated
9203
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@kie/git-backporting",
|
"name": "@kie/git-backporting",
|
||||||
"version": "4.8.5",
|
"version": "4.7.1",
|
||||||
"description": "Git backporting is a tool to execute automatic pull request git backporting.",
|
"description": "Git backporting is a tool to execute automatic pull request git backporting.",
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -55,7 +55,7 @@
|
||||||
"@gitbeaker/rest": "^39.1.0",
|
"@gitbeaker/rest": "^39.1.0",
|
||||||
"@kie/mock-github": "^1.1.0",
|
"@kie/mock-github": "^1.1.0",
|
||||||
"@octokit/webhooks-types": "^6.8.0",
|
"@octokit/webhooks-types": "^6.8.0",
|
||||||
"@release-it/conventional-changelog": "^10.0.0",
|
"@release-it/conventional-changelog": "^7.0.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/jest": "^29.2.4",
|
"@types/jest": "^29.2.4",
|
||||||
"@types/node": "^18.11.17",
|
"@types/node": "^18.11.17",
|
||||||
|
@ -66,7 +66,7 @@
|
||||||
"husky": "^8.0.2",
|
"husky": "^8.0.2",
|
||||||
"jest": "^29.0.0",
|
"jest": "^29.0.0",
|
||||||
"jest-sonar-reporter": "^2.0.0",
|
"jest-sonar-reporter": "^2.0.0",
|
||||||
"release-it": "^18.1.2",
|
"release-it": "^16.1.3",
|
||||||
"semver": "^7.3.8",
|
"semver": "^7.3.8",
|
||||||
"ts-jest": "^29.0.0",
|
"ts-jest": "^29.0.0",
|
||||||
"ts-node": "^10.8.1",
|
"ts-node": "^10.8.1",
|
||||||
|
|
|
@ -44,12 +44,10 @@ export default abstract class ArgsParser {
|
||||||
labels: this.getOrDefault(args.labels, []),
|
labels: this.getOrDefault(args.labels, []),
|
||||||
inheritLabels: this.getOrDefault(args.inheritLabels, false),
|
inheritLabels: this.getOrDefault(args.inheritLabels, false),
|
||||||
squash: this.getOrDefault(args.squash, true),
|
squash: this.getOrDefault(args.squash, true),
|
||||||
autoNoSquash: this.getOrDefault(args.autoNoSquash, false),
|
|
||||||
strategy: this.getOrDefault(args.strategy),
|
strategy: this.getOrDefault(args.strategy),
|
||||||
strategyOption: this.getOrDefault(args.strategyOption),
|
strategyOption: this.getOrDefault(args.strategyOption),
|
||||||
cherryPickOptions: this.getOrDefault(args.cherryPickOptions),
|
cherryPickOptions: this.getOrDefault(args.cherryPickOptions),
|
||||||
comments: this.getOrDefault(args.comments),
|
comments: this.getOrDefault(args.comments)
|
||||||
enableErrorNotification: this.getOrDefault(args.enableErrorNotification, false),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -50,7 +50,7 @@ export function getAsSemicolonSeparatedList(value: string): string[] | undefined
|
||||||
return trimmed !== "" ? trimmed.split(";").map(v => v.trim()) : undefined;
|
return trimmed !== "" ? trimmed.split(";").map(v => v.trim()) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAsBooleanOrUndefined(value: string): boolean | undefined {
|
export function getAsBooleanOrDefault(value: string): boolean | undefined {
|
||||||
const trimmed = value.trim();
|
const trimmed = value.trim();
|
||||||
return trimmed !== "" ? trimmed.toLowerCase() === "true" : undefined;
|
return trimmed !== "" ? trimmed.toLowerCase() === "true" : undefined;
|
||||||
}
|
}
|
|
@ -22,11 +22,9 @@ export interface Args {
|
||||||
inheritReviewers?: boolean, // if true and reviewers == [] then inherit reviewers from original pr
|
inheritReviewers?: boolean, // if true and reviewers == [] then inherit reviewers from original pr
|
||||||
labels?: string[], // backport pr labels
|
labels?: string[], // backport pr labels
|
||||||
inheritLabels?: boolean, // if true inherit labels from original pr
|
inheritLabels?: boolean, // if true inherit labels from original pr
|
||||||
squash?: boolean,
|
squash?: boolean, // if false use squashed/merged commit otherwise backport all commits as part of the pr
|
||||||
autoNoSquash?: boolean,
|
|
||||||
strategy?: string, // cherry-pick merge strategy
|
strategy?: string, // cherry-pick merge strategy
|
||||||
strategyOption?: string, // cherry-pick merge strategy option
|
strategyOption?: string, // cherry-pick merge strategy option
|
||||||
cherryPickOptions?: string, // additional cherry-pick options
|
cherryPickOptions?: string, // additional cherry-pick options
|
||||||
comments?: string[], // additional comments to be posted
|
comments?: string[], // additional comments to be posted
|
||||||
enableErrorNotification?: boolean, // enable the error notification on original pull request
|
|
||||||
}
|
}
|
|
@ -28,13 +28,11 @@ export default class CLIArgsParser extends ArgsParser {
|
||||||
.option("--no-inherit-reviewers", "if provided and reviewers option is empty then inherit them from original pull request")
|
.option("--no-inherit-reviewers", "if provided and reviewers option is empty then inherit them from original pull request")
|
||||||
.option("--labels <labels>", "comma separated list of labels to be assigned to the backported pull request", getAsCommaSeparatedList)
|
.option("--labels <labels>", "comma separated list of labels to be assigned to the backported pull request", getAsCommaSeparatedList)
|
||||||
.option("--inherit-labels", "if true the backported pull request will inherit labels from the original one")
|
.option("--inherit-labels", "if true the backported pull request will inherit labels from the original one")
|
||||||
.option("--no-squash", "backport all commits found in the pull request. The default behavior is to only backport the first commit that was merged in the base branch")
|
.option("--no-squash", "if provided the tool will backport all commits as part of the pull request")
|
||||||
.option("--auto-no-squash", "if the pull request was merged or is open, backport all commits. If the pull request commits were squashed, backport the squashed commit.")
|
|
||||||
.option("--strategy <strategy>", "cherry-pick merge strategy, default to 'recursive'", undefined)
|
.option("--strategy <strategy>", "cherry-pick merge strategy, default to 'recursive'", undefined)
|
||||||
.option("--strategy-option <strategy-option>", "cherry-pick merge strategy option, default to 'theirs'")
|
.option("--strategy-option <strategy-option>", "cherry-pick merge strategy option, default to 'theirs'")
|
||||||
.option("--cherry-pick-options <options>", "additional cherry-pick options")
|
.option("--cherry-pick-options <options>", "additional cherry-pick options")
|
||||||
.option("--comments <comments>", "semicolon separated list of additional comments to be posted to the backported pull request", getAsSemicolonSeparatedList)
|
.option("--comments <comments>", "semicolon separated list of additional comments to be posted to the backported pull request", getAsSemicolonSeparatedList)
|
||||||
.option("--enable-err-notification", "if true, enable the error notification as comment on the original pull request")
|
|
||||||
.option("-cf, --config-file <config-file>", "configuration file containing all valid options, the json must match Args interface");
|
.option("-cf, --config-file <config-file>", "configuration file containing all valid options, the json must match Args interface");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,12 +66,10 @@ export default class CLIArgsParser extends ArgsParser {
|
||||||
labels: opts.labels,
|
labels: opts.labels,
|
||||||
inheritLabels: opts.inheritLabels,
|
inheritLabels: opts.inheritLabels,
|
||||||
squash: opts.squash,
|
squash: opts.squash,
|
||||||
autoNoSquash: opts.autoNoSquash,
|
|
||||||
strategy: opts.strategy,
|
strategy: opts.strategy,
|
||||||
strategyOption: opts.strategyOption,
|
strategyOption: opts.strategyOption,
|
||||||
cherryPickOptions: opts.cherryPickOptions,
|
cherryPickOptions: opts.cherryPickOptions,
|
||||||
comments: opts.comments,
|
comments: opts.comments,
|
||||||
enableErrorNotification: opts.enableErrNotification,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import ArgsParser from "@bp/service/args/args-parser";
|
import ArgsParser from "@bp/service/args/args-parser";
|
||||||
import { Args } from "@bp/service/args/args.types";
|
import { Args } from "@bp/service/args/args.types";
|
||||||
import { getInput } from "@actions/core";
|
import { getInput } from "@actions/core";
|
||||||
import { getAsBooleanOrUndefined, getAsCleanedCommaSeparatedList, getAsCommaSeparatedList, getAsSemicolonSeparatedList, getOrUndefined, readConfigFile } from "@bp/service/args/args-utils";
|
import { getAsBooleanOrDefault, getAsCleanedCommaSeparatedList, getAsCommaSeparatedList, getAsSemicolonSeparatedList, getOrUndefined, readConfigFile } from "@bp/service/args/args-utils";
|
||||||
|
|
||||||
export default class GHAArgsParser extends ArgsParser {
|
export default class GHAArgsParser extends ArgsParser {
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ export default class GHAArgsParser extends ArgsParser {
|
||||||
args = readConfigFile(configFile);
|
args = readConfigFile(configFile);
|
||||||
} else {
|
} else {
|
||||||
args = {
|
args = {
|
||||||
dryRun: getAsBooleanOrUndefined(getInput("dry-run")),
|
dryRun: getAsBooleanOrDefault(getInput("dry-run")),
|
||||||
auth: getOrUndefined(getInput("auth")),
|
auth: getOrUndefined(getInput("auth")),
|
||||||
pullRequest: getInput("pull-request"),
|
pullRequest: getInput("pull-request"),
|
||||||
targetBranch: getOrUndefined(getInput("target-branch")),
|
targetBranch: getOrUndefined(getInput("target-branch")),
|
||||||
|
@ -28,16 +28,14 @@ export default class GHAArgsParser extends ArgsParser {
|
||||||
bpBranchName: getOrUndefined(getInput("bp-branch-name")),
|
bpBranchName: getOrUndefined(getInput("bp-branch-name")),
|
||||||
reviewers: getAsCleanedCommaSeparatedList(getInput("reviewers")),
|
reviewers: getAsCleanedCommaSeparatedList(getInput("reviewers")),
|
||||||
assignees: getAsCleanedCommaSeparatedList(getInput("assignees")),
|
assignees: getAsCleanedCommaSeparatedList(getInput("assignees")),
|
||||||
inheritReviewers: !getAsBooleanOrUndefined(getInput("no-inherit-reviewers")),
|
inheritReviewers: !getAsBooleanOrDefault(getInput("no-inherit-reviewers")),
|
||||||
labels: getAsCommaSeparatedList(getInput("labels")),
|
labels: getAsCommaSeparatedList(getInput("labels")),
|
||||||
inheritLabels: getAsBooleanOrUndefined(getInput("inherit-labels")),
|
inheritLabels: getAsBooleanOrDefault(getInput("inherit-labels")),
|
||||||
squash: !getAsBooleanOrUndefined(getInput("no-squash")),
|
squash: !getAsBooleanOrDefault(getInput("no-squash")),
|
||||||
autoNoSquash: getAsBooleanOrUndefined(getInput("auto-no-squash")),
|
|
||||||
strategy: getOrUndefined(getInput("strategy")),
|
strategy: getOrUndefined(getInput("strategy")),
|
||||||
strategyOption: getOrUndefined(getInput("strategy-option")),
|
strategyOption: getOrUndefined(getInput("strategy-option")),
|
||||||
cherryPickOptions: getOrUndefined(getInput("cherry-pick-options")),
|
cherryPickOptions: getOrUndefined(getInput("cherry-pick-options")),
|
||||||
comments: getAsSemicolonSeparatedList(getInput("comments")),
|
comments: getAsSemicolonSeparatedList(getInput("comments")),
|
||||||
enableErrorNotification: getAsBooleanOrUndefined(getInput("enable-err-notification")),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,11 @@
|
||||||
|
|
||||||
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
||||||
|
|
||||||
export const MESSAGE_ERROR_PLACEHOLDER = "{{error}}";
|
|
||||||
export const MESSAGE_TARGET_BRANCH_PLACEHOLDER = "{{target-branch}}";
|
|
||||||
|
|
||||||
export interface LocalGit {
|
export interface LocalGit {
|
||||||
user: string, // local git user
|
user: string, // local git user
|
||||||
email: string, // local git email
|
email: string, // local git email
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ErrorNotification {
|
|
||||||
enabled: boolean, // if the error notification is enabled
|
|
||||||
message: string, // notification message, placeholder {{error}} will be replaced with actual error
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal configuration object
|
* Internal configuration object
|
||||||
*/
|
*/
|
||||||
|
@ -28,7 +20,6 @@ export interface Configs {
|
||||||
cherryPickOptions?: string, // additional cherry-pick options
|
cherryPickOptions?: string, // additional cherry-pick options
|
||||||
originalPullRequest: GitPullRequest,
|
originalPullRequest: GitPullRequest,
|
||||||
backportPullRequests: BackportPullRequest[],
|
backportPullRequests: BackportPullRequest[],
|
||||||
errorNotification: ErrorNotification,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum AuthTokenId {
|
export enum AuthTokenId {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { getAsCleanedCommaSeparatedList, getAsCommaSeparatedList } from "@bp/service/args/args-utils";
|
import { getAsCleanedCommaSeparatedList, getAsCommaSeparatedList } from "@bp/service/args/args-utils";
|
||||||
import { Args } from "@bp/service/args/args.types";
|
import { Args } from "@bp/service/args/args.types";
|
||||||
import ConfigsParser from "@bp/service/configs/configs-parser";
|
import ConfigsParser from "@bp/service/configs/configs-parser";
|
||||||
import { Configs, MESSAGE_TARGET_BRANCH_PLACEHOLDER } from "@bp/service/configs/configs.types";
|
import { Configs } from "@bp/service/configs/configs.types";
|
||||||
import GitClient from "@bp/service/git/git-client";
|
import GitClient from "@bp/service/git/git-client";
|
||||||
import GitClientFactory from "@bp/service/git/git-client-factory";
|
import GitClientFactory from "@bp/service/git/git-client-factory";
|
||||||
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types";
|
||||||
|
@ -17,11 +17,8 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||||
|
|
||||||
public async parse(args: Args): Promise<Configs> {
|
public async parse(args: Args): Promise<Configs> {
|
||||||
let pr: GitPullRequest;
|
let pr: GitPullRequest;
|
||||||
if (args.autoNoSquash) {
|
|
||||||
args.squash = undefined;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
pr = await this.gitClient.getPullRequestFromUrl(args.pullRequest, args.squash);
|
pr = await this.gitClient.getPullRequestFromUrl(args.pullRequest, args.squash!);
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
this.logger.error("Something went wrong retrieving pull request");
|
this.logger.error("Something went wrong retrieving pull request");
|
||||||
throw error;
|
throw error;
|
||||||
|
@ -58,11 +55,7 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||||
git: {
|
git: {
|
||||||
user: args.gitUser ?? this.gitClient.getDefaultGitUser(),
|
user: args.gitUser ?? this.gitClient.getDefaultGitUser(),
|
||||||
email: args.gitEmail ?? this.gitClient.getDefaultGitEmail(),
|
email: args.gitEmail ?? this.gitClient.getDefaultGitEmail(),
|
||||||
},
|
}
|
||||||
errorNotification: {
|
|
||||||
enabled: args.enableErrorNotification ?? false,
|
|
||||||
message: this.getDefaultErrorComment(),
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,11 +63,6 @@ export default class PullRequestConfigsParser extends ConfigsParser {
|
||||||
return "bp";
|
return "bp";
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDefaultErrorComment(): string {
|
|
||||||
// TODO: fetch from arg or set default with placeholder {{error}}
|
|
||||||
return `The backport to \`${MESSAGE_TARGET_BRANCH_PLACEHOLDER}\` failed. Check the latest run for more details.`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse the provided labels and return a list of target branches
|
* Parse the provided labels and return a list of target branches
|
||||||
* obtained by applying the provided pattern as regular expression extractor
|
* obtained by applying the provided pattern as regular expression extractor
|
||||||
|
|
|
@ -68,15 +68,6 @@ export default class GitCLIService {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.logger.info(`Folder ${to} already exist. Won't clone`);
|
this.logger.info(`Folder ${to} already exist. Won't clone`);
|
||||||
|
|
||||||
// ensure the working tree is properly reset - no stale changes
|
|
||||||
// from previous (failed) backport
|
|
||||||
const ongoingCherryPick = await this.anyConflict(to);
|
|
||||||
if (ongoingCherryPick) {
|
|
||||||
this.logger.warn("Found previously failed cherry-pick, aborting it");
|
|
||||||
await this.git(to).raw(["cherry-pick", "--abort"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// checkout to the proper branch
|
// checkout to the proper branch
|
||||||
this.logger.info(`Checking out branch ${branch}`);
|
this.logger.info(`Checking out branch ${branch}`);
|
||||||
await this.git(to).checkout(branch);
|
await this.git(to).checkout(branch);
|
||||||
|
@ -140,21 +131,6 @@ export default class GitCLIService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check whether there are some conflicts in the current working directory
|
|
||||||
* which means there is an ongoing cherry-pick that did not complete successfully
|
|
||||||
* @param cwd repository in which the check should be performed
|
|
||||||
* @return true if there is some conflict, false otherwise
|
|
||||||
*/
|
|
||||||
async anyConflict(cwd: string): Promise<boolean> {
|
|
||||||
const status = await this.git(cwd).status();
|
|
||||||
if (status.conflicted.length > 0) {
|
|
||||||
this.logger.debug(`Found conflicts in branch ${status.current}`);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push a branch to a remote
|
* Push a branch to a remote
|
||||||
* @param cwd repository in which the push should be performed
|
* @param cwd repository in which the push should be performed
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/
|
||||||
* @param squash if true keep just one single commit, otherwise get the full list
|
* @param squash if true keep just one single commit, otherwise get the full list
|
||||||
* @returns {Promise<PullRequest>}
|
* @returns {Promise<PullRequest>}
|
||||||
*/
|
*/
|
||||||
getPullRequest(owner: string, repo: string, prNumber: number, squash: boolean | undefined): Promise<GitPullRequest>;
|
getPullRequest(owner: string, repo: string, prNumber: number, squash: boolean): Promise<GitPullRequest>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a pull request object from the underneath git service
|
* Get a pull request object from the underneath git service
|
||||||
|
@ -33,7 +33,7 @@ import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/
|
||||||
* @param squash if true keep just one single commit, otherwise get the full list
|
* @param squash if true keep just one single commit, otherwise get the full list
|
||||||
* @returns {Promise<PullRequest>}
|
* @returns {Promise<PullRequest>}
|
||||||
*/
|
*/
|
||||||
getPullRequestFromUrl(prUrl: string, squash: boolean | undefined): Promise<GitPullRequest>;
|
getPullRequestFromUrl(prUrl: string, squash: boolean): Promise<GitPullRequest>;
|
||||||
|
|
||||||
// WRITE
|
// WRITE
|
||||||
|
|
||||||
|
@ -44,11 +44,4 @@ import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/
|
||||||
*/
|
*/
|
||||||
createPullRequest(backport: BackportPullRequest): Promise<string>;
|
createPullRequest(backport: BackportPullRequest): Promise<string>;
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new comment on the provided pull request
|
|
||||||
* @param prUrl pull request's URL
|
|
||||||
* @param comment comment body
|
|
||||||
*/
|
|
||||||
createPullRequestComment(prUrl: string, comment: string): Promise<string | undefined>;
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,3 @@
|
||||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
|
||||||
import { GitClientType } from "@bp/service/git/git.types";
|
import { GitClientType } from "@bp/service/git/git.types";
|
||||||
import { AuthTokenId } from "@bp/service/configs/configs.types";
|
import { AuthTokenId } from "@bp/service/configs/configs.types";
|
||||||
|
|
||||||
|
@ -42,29 +41,6 @@ export const inferGitApiUrl = (prUrl: string, apiVersion = "v4"): string => {
|
||||||
return `${baseUrl}/api/${apiVersion}`;
|
return `${baseUrl}/api/${apiVersion}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Infer the value of the squash option
|
|
||||||
* @param open true if the pull/merge request is still open
|
|
||||||
* @param squash_commit undefined or null if the pull/merge request was merged, the sha of the squashed commit if it was squashed
|
|
||||||
* @returns true if a single commit must be cherry-picked, false if all merged commits must be cherry-picked
|
|
||||||
*/
|
|
||||||
export const inferSquash = (open: boolean, squash_commit: string | undefined | null): boolean => {
|
|
||||||
const logger = LoggerServiceFactory.getLogger();
|
|
||||||
|
|
||||||
if (open) {
|
|
||||||
logger.debug("cherry-pick all commits because they have not been merged (or squashed) in the base branch yet");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
if (squash_commit) {
|
|
||||||
logger.debug(`cherry-pick the squashed commit ${squash_commit}`);
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
logger.debug("cherry-pick the merged commit(s)");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the git token from env variable, the default is taken from GIT_TOKEN env.
|
* Retrieve the git token from env variable, the default is taken from GIT_TOKEN env.
|
||||||
* All specific git env variable have precedence and override the default one.
|
* All specific git env variable have precedence and override the default one.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
export interface GitPullRequest {
|
export interface GitPullRequest {
|
||||||
number?: number,
|
number?: number,
|
||||||
author: string,
|
author: string,
|
||||||
url: string,
|
url?: string,
|
||||||
htmlUrl?: string,
|
htmlUrl?: string,
|
||||||
state?: GitRepoState,
|
state?: GitRepoState,
|
||||||
merged?: boolean,
|
merged?: boolean,
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import GitClient from "@bp/service/git/git-client";
|
import GitClient from "@bp/service/git/git-client";
|
||||||
import { inferSquash } from "@bp/service/git/git-util";
|
|
||||||
import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/git/git.types";
|
import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/git/git.types";
|
||||||
import GitHubMapper from "@bp/service/git/github/github-mapper";
|
import GitHubMapper from "@bp/service/git/github/github-mapper";
|
||||||
import OctokitFactory from "@bp/service/git/github/octokit-factory";
|
import OctokitFactory from "@bp/service/git/github/octokit-factory";
|
||||||
|
@ -38,7 +37,7 @@ export default class GitHubClient implements GitClient {
|
||||||
return "noreply@github.com";
|
return "noreply@github.com";
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPullRequest(owner: string, repo: string, prNumber: number, squash: boolean | undefined): Promise<GitPullRequest> {
|
async getPullRequest(owner: string, repo: string, prNumber: number, squash = true): Promise<GitPullRequest> {
|
||||||
this.logger.debug(`Fetching pull request ${owner}/${repo}/${prNumber}`);
|
this.logger.debug(`Fetching 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,
|
||||||
|
@ -46,22 +45,6 @@ export default class GitHubClient implements GitClient {
|
||||||
pull_number: prNumber,
|
pull_number: prNumber,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (squash === undefined) {
|
|
||||||
let commit_sha: string | undefined = undefined;
|
|
||||||
const open: boolean = data.state == "open";
|
|
||||||
if (!open) {
|
|
||||||
const commit = await this.octokit.rest.git.getCommit({
|
|
||||||
owner: owner,
|
|
||||||
repo: repo,
|
|
||||||
commit_sha: (data.merge_commit_sha as string),
|
|
||||||
});
|
|
||||||
if (commit.data.parents.length === 1) {
|
|
||||||
commit_sha = (data.merge_commit_sha as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
squash = inferSquash(open, commit_sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
const commits: string[] = [];
|
const commits: string[] = [];
|
||||||
if (!squash) {
|
if (!squash) {
|
||||||
// fetch all commits
|
// fetch all commits
|
||||||
|
@ -73,11 +56,6 @@ export default class GitHubClient implements GitClient {
|
||||||
});
|
});
|
||||||
|
|
||||||
commits.push(...data.map(c => c.sha));
|
commits.push(...data.map(c => c.sha));
|
||||||
if (this.isForCodeberg) {
|
|
||||||
// For some reason, even though Codeberg advertises API compatibility
|
|
||||||
// with GitHub, it returns commits in reversed order.
|
|
||||||
commits.reverse();
|
|
||||||
}
|
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
throw new Error(`Failed to retrieve commits for pull request n. ${prNumber}`);
|
throw new Error(`Failed to retrieve commits for pull request n. ${prNumber}`);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +64,7 @@ export default class GitHubClient implements GitClient {
|
||||||
return this.mapper.mapPullRequest(data as PullRequest, commits);
|
return this.mapper.mapPullRequest(data as PullRequest, commits);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPullRequestFromUrl(prUrl: string, squash: boolean | undefined): Promise<GitPullRequest> {
|
async getPullRequestFromUrl(prUrl: string, squash = true): Promise<GitPullRequest> {
|
||||||
const { owner, project, id } = this.extractPullRequestData(prUrl);
|
const { owner, project, id } = this.extractPullRequestData(prUrl);
|
||||||
return this.getPullRequest(owner, project, id, squash);
|
return this.getPullRequest(owner, project, id, squash);
|
||||||
}
|
}
|
||||||
|
@ -163,29 +141,6 @@ export default class GitHubClient implements GitClient {
|
||||||
return data.html_url;
|
return data.html_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createPullRequestComment(prUrl: string, comment: string): Promise<string | undefined> {
|
|
||||||
let commentUrl: string | undefined = undefined;
|
|
||||||
try {
|
|
||||||
const { owner, project, id } = this.extractPullRequestData(prUrl);
|
|
||||||
const { data } = await this.octokit.issues.createComment({
|
|
||||||
owner: owner,
|
|
||||||
repo: project,
|
|
||||||
issue_number: id,
|
|
||||||
body: comment
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
throw new Error("Pull request comment creation failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
commentUrl = data.url;
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error creating comment on pull request ${prUrl}: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return commentUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTILS
|
// UTILS
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,8 +24,8 @@ export default class GitHubMapper implements GitResponseMapper<PullRequest, "ope
|
||||||
state: this.mapGitState(pr.state), // TODO fix using custom mapper
|
state: this.mapGitState(pr.state), // TODO fix using custom mapper
|
||||||
merged: pr.merged ?? false,
|
merged: pr.merged ?? false,
|
||||||
mergedBy: pr.merged_by?.login,
|
mergedBy: pr.merged_by?.login,
|
||||||
reviewers: pr.requested_reviewers?.filter(r => 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)) ?? [],
|
||||||
assignees: pr.assignees?.filter(r => r && "login" in r).map(r => r.login) ?? [],
|
assignees: pr.assignees?.filter(r => "login" in r).map(r => r.login) ?? [],
|
||||||
labels: pr.labels?.map(l => l.name) ?? [],
|
labels: pr.labels?.map(l => l.name) ?? [],
|
||||||
sourceRepo: await this.mapSourceRepo(pr),
|
sourceRepo: await this.mapSourceRepo(pr),
|
||||||
targetRepo: await this.mapTargetRepo(pr),
|
targetRepo: await this.mapTargetRepo(pr),
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import LoggerService from "@bp/service/logger/logger-service";
|
import LoggerService from "@bp/service/logger/logger-service";
|
||||||
import GitClient from "@bp/service/git/git-client";
|
import GitClient from "@bp/service/git/git-client";
|
||||||
import { inferSquash } from "@bp/service/git/git-util";
|
|
||||||
import { GitPullRequest, BackportPullRequest, GitClientType } from "@bp/service/git/git.types";
|
import { GitPullRequest, BackportPullRequest, GitClientType } from "@bp/service/git/git.types";
|
||||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
||||||
import { CommitSchema, MergeRequestSchema, UserSchema } from "@gitbeaker/rest";
|
import { CommitSchema, MergeRequestSchema, UserSchema } from "@gitbeaker/rest";
|
||||||
|
@ -46,15 +45,9 @@ export default class GitLabClient implements GitClient {
|
||||||
// READ
|
// READ
|
||||||
|
|
||||||
// example: <host>/api/v4/projects/<namespace>%2Fbackporting-example/merge_requests/1
|
// example: <host>/api/v4/projects/<namespace>%2Fbackporting-example/merge_requests/1
|
||||||
async getPullRequest(namespace: string, repo: string, mrNumber: number, squash: boolean | undefined): Promise<GitPullRequest> {
|
async getPullRequest(namespace: string, repo: string, mrNumber: number, squash = true): Promise<GitPullRequest> {
|
||||||
const projectId = this.getProjectId(namespace, repo);
|
const projectId = this.getProjectId(namespace, repo);
|
||||||
const url = `/projects/${projectId}/merge_requests/${mrNumber}`;
|
const { data } = await this.client.get(`/projects/${projectId}/merge_requests/${mrNumber}`);
|
||||||
this.logger.debug(`Fetching pull request ${url}`);
|
|
||||||
const { data } = await this.client.get(`${url}`);
|
|
||||||
|
|
||||||
if (squash === undefined) {
|
|
||||||
squash = inferSquash(data.state === "opened", data.squash_commit_sha);
|
|
||||||
}
|
|
||||||
|
|
||||||
const commits: string[] = [];
|
const commits: string[] = [];
|
||||||
if (!squash) {
|
if (!squash) {
|
||||||
|
@ -72,7 +65,7 @@ export default class GitLabClient implements GitClient {
|
||||||
return this.mapper.mapPullRequest(data as MergeRequestSchema, commits);
|
return this.mapper.mapPullRequest(data as MergeRequestSchema, commits);
|
||||||
}
|
}
|
||||||
|
|
||||||
getPullRequestFromUrl(mrUrl: string, squash: boolean | undefined): Promise<GitPullRequest> {
|
getPullRequestFromUrl(mrUrl: string, squash = true): Promise<GitPullRequest> {
|
||||||
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
|
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
|
||||||
return this.getPullRequest(namespace, project, id, squash);
|
return this.getPullRequest(namespace, project, id, squash);
|
||||||
}
|
}
|
||||||
|
@ -164,29 +157,6 @@ export default class GitLabClient implements GitClient {
|
||||||
return mr.web_url;
|
return mr.web_url;
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://docs.gitlab.com/ee/api/notes.html#create-new-issue-note
|
|
||||||
async createPullRequestComment(mrUrl: string, comment: string): Promise<string | undefined> {
|
|
||||||
const commentUrl: string | undefined = undefined;
|
|
||||||
try{
|
|
||||||
const { namespace, project, id } = this.extractMergeRequestData(mrUrl);
|
|
||||||
const projectId = this.getProjectId(namespace, project);
|
|
||||||
|
|
||||||
const { data } = await this.client.post(`/projects/${projectId}/merge_requests/${id}/notes`, {
|
|
||||||
body: comment,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!data) {
|
|
||||||
throw new Error("Merge request comment creation failed");
|
|
||||||
}
|
|
||||||
} catch(error) {
|
|
||||||
this.logger.error(`Error creating comment on merge request ${mrUrl}: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return commentUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTILS
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve a gitlab user given its username
|
* Retrieve a gitlab user given its username
|
||||||
* @param username
|
* @param username
|
||||||
|
|
|
@ -16,10 +16,6 @@ export default class ConsoleLoggerService implements LoggerService {
|
||||||
this.context = newContext;
|
this.context = newContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
getContext(): string | undefined {
|
|
||||||
return this.context;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearContext() {
|
clearContext() {
|
||||||
this.context = undefined;
|
this.context = undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@ export default interface LoggerService {
|
||||||
|
|
||||||
setContext(newContext: string): void;
|
setContext(newContext: string): void;
|
||||||
|
|
||||||
getContext(): string | undefined;
|
|
||||||
|
|
||||||
clearContext(): void;
|
clearContext(): void;
|
||||||
|
|
||||||
trace(message: string): void;
|
trace(message: string): void;
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
import { MESSAGE_ERROR_PLACEHOLDER, MESSAGE_TARGET_BRANCH_PLACEHOLDER } from "@bp/service/configs/configs.types";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject the error message in the provided `message`.
|
|
||||||
* This is injected in place of the MESSAGE_ERROR_PLACEHOLDER placeholder
|
|
||||||
* @param message string that needs to be updated
|
|
||||||
* @param errMsg the error message that needs to be injected
|
|
||||||
*/
|
|
||||||
export const injectError = (message: string, errMsg: string): string => {
|
|
||||||
return message.replace(MESSAGE_ERROR_PLACEHOLDER, errMsg);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inject the target branch into the provided `message`.
|
|
||||||
* This is injected in place of the MESSAGE_TARGET_BRANCH_PLACEHOLDER placeholder
|
|
||||||
* @param message string that needs to be updated
|
|
||||||
* @param targetBranch the target branch to inject
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export const injectTargetBranch = (message: string, targetBranch: string): string => {
|
|
||||||
return message.replace(MESSAGE_TARGET_BRANCH_PLACEHOLDER, targetBranch);
|
|
||||||
};
|
|
|
@ -9,7 +9,6 @@ import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/
|
||||||
import LoggerService from "@bp/service/logger/logger-service";
|
import LoggerService from "@bp/service/logger/logger-service";
|
||||||
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
import LoggerServiceFactory from "@bp/service/logger/logger-service-factory";
|
||||||
import { inferGitClient, inferGitApiUrl, getGitTokenFromEnv } from "@bp/service/git/git-util";
|
import { inferGitClient, inferGitApiUrl, getGitTokenFromEnv } from "@bp/service/git/git-util";
|
||||||
import { injectError, injectTargetBranch } from "./runner-util";
|
|
||||||
|
|
||||||
interface Git {
|
interface Git {
|
||||||
gitClientType: GitClientType;
|
gitClientType: GitClientType;
|
||||||
|
@ -93,12 +92,6 @@ export default class Runner {
|
||||||
});
|
});
|
||||||
} catch(error) {
|
} catch(error) {
|
||||||
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
|
this.logger.error(`Something went wrong backporting to ${pr.base}: ${error}`);
|
||||||
if (!configs.dryRun && configs.errorNotification.enabled && configs.errorNotification.message.length > 0) {
|
|
||||||
// notify the failure as comment in the original pull request
|
|
||||||
let comment = injectError(configs.errorNotification.message, error as string);
|
|
||||||
comment = injectTargetBranch(comment, pr.base);
|
|
||||||
await gitApi.createPullRequestComment(configs.originalPullRequest.url, comment);
|
|
||||||
}
|
|
||||||
failures.push(error as string);
|
failures.push(error as string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,19 +133,20 @@ export default class Runner {
|
||||||
|
|
||||||
// 5. create new branch from target one and checkout
|
// 5. create new branch from target one and checkout
|
||||||
this.logger.debug("Creating local branch..");
|
this.logger.debug("Creating local branch..");
|
||||||
|
|
||||||
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
|
await git.gitCli.createLocalBranch(configs.folder, backportPR.head);
|
||||||
|
|
||||||
// 6. fetch pull request remote if source owner != target owner or pull request still open
|
// 6. fetch pull request remote if source owner != target owner or pull request still open
|
||||||
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
|
if (configs.originalPullRequest.sourceRepo.owner !== configs.originalPullRequest.targetRepo.owner ||
|
||||||
configs.originalPullRequest.state === "open") {
|
configs.originalPullRequest.state === "open") {
|
||||||
this.logger.debug("Fetching pull request remote..");
|
this.logger.debug("Fetching pull request remote..");
|
||||||
const prefix = git.gitClientType === GitClientType.GITLAB ? "merge-requests" : "pull" ; // default is for gitlab
|
const prefix = git.gitClientType === GitClientType.GITLAB ? "merge-requests" : "pull" ; // default is for gitlab
|
||||||
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
await git.gitCli.fetch(configs.folder, `${prefix}/${configs.originalPullRequest.number}/head:pr/${configs.originalPullRequest.number}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. apply all changes to the new branch
|
// 7. apply all changes to the new branch
|
||||||
this.logger.debug("Cherry picking commits..");
|
this.logger.debug("Cherry picking commits..");
|
||||||
for (const sha of originalPR.commits) {
|
for (const sha of originalPR.commits.reverse()!) {
|
||||||
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
|
await git.gitCli.cherryPick(configs.folder, sha, configs.mergeStrategy, configs.mergeStrategyOption, configs.cherryPickOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,9 @@ describe("cli args parser", () => {
|
||||||
expect(args.labels).toEqual([]);
|
expect(args.labels).toEqual([]);
|
||||||
expect(args.inheritLabels).toEqual(false);
|
expect(args.inheritLabels).toEqual(false);
|
||||||
expect(args.squash).toEqual(true);
|
expect(args.squash).toEqual(true);
|
||||||
expect(args.autoNoSquash).toEqual(false);
|
|
||||||
expect(args.strategy).toEqual(undefined);
|
expect(args.strategy).toEqual(undefined);
|
||||||
expect(args.strategyOption).toEqual(undefined);
|
expect(args.strategyOption).toEqual(undefined);
|
||||||
expect(args.cherryPickOptions).toEqual(undefined);
|
expect(args.cherryPickOptions).toEqual(undefined);
|
||||||
expect(args.enableErrorNotification).toEqual(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("with config file [default, short]", () => {
|
test("with config file [default, short]", () => {
|
||||||
|
@ -111,11 +109,9 @@ describe("cli args parser", () => {
|
||||||
expect(args.labels).toEqual([]);
|
expect(args.labels).toEqual([]);
|
||||||
expect(args.inheritLabels).toEqual(false);
|
expect(args.inheritLabels).toEqual(false);
|
||||||
expect(args.squash).toEqual(true);
|
expect(args.squash).toEqual(true);
|
||||||
expect(args.autoNoSquash).toEqual(false);
|
|
||||||
expect(args.strategy).toEqual(undefined);
|
expect(args.strategy).toEqual(undefined);
|
||||||
expect(args.strategyOption).toEqual(undefined);
|
expect(args.strategyOption).toEqual(undefined);
|
||||||
expect(args.cherryPickOptions).toEqual(undefined);
|
expect(args.cherryPickOptions).toEqual(undefined);
|
||||||
expect(args.enableErrorNotification).toEqual(false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("valid execution [default, long]", () => {
|
test("valid execution [default, long]", () => {
|
||||||
|
@ -525,17 +521,4 @@ describe("cli args parser", () => {
|
||||||
|
|
||||||
expect(() => parser.parse()).toThrowError("Missing option: pull request must be provided");
|
expect(() => parser.parse()).toThrowError("Missing option: pull request must be provided");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("enable error notification flag", () => {
|
|
||||||
addProcessArgs([
|
|
||||||
"-tb",
|
|
||||||
"target, old",
|
|
||||||
"-pr",
|
|
||||||
"https://localhost/whatever/pulls/1",
|
|
||||||
"--enable-err-notification",
|
|
||||||
]);
|
|
||||||
|
|
||||||
const args: Args = parser.parse();
|
|
||||||
expect(args.enableErrorNotification).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -295,6 +295,7 @@ describe("gha args parser", () => {
|
||||||
expect(args.cherryPickOptions).toEqual(undefined);
|
expect(args.cherryPickOptions).toEqual(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
test("invalid execution with empty target branch", () => {
|
test("invalid execution with empty target branch", () => {
|
||||||
spyGetInput({
|
spyGetInput({
|
||||||
"target-branch": " ",
|
"target-branch": " ",
|
||||||
|
@ -319,15 +320,4 @@ describe("gha args parser", () => {
|
||||||
|
|
||||||
expect(() => parser.parse()).toThrowError("Missing option: pull request must be provided");
|
expect(() => parser.parse()).toThrowError("Missing option: pull request must be provided");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("enable error notification flag", () => {
|
|
||||||
spyGetInput({
|
|
||||||
"target-branch": "target,old",
|
|
||||||
"pull-request": "https://localhost/whatever/pulls/1",
|
|
||||||
"enable-err-notification": "true"
|
|
||||||
});
|
|
||||||
|
|
||||||
const args: Args = parser.parse();
|
|
||||||
expect(args.enableErrorNotification).toEqual(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -57,7 +57,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -202,7 +202,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -275,7 +275,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
|
|
@ -89,7 +89,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -139,10 +139,6 @@ describe("github pull request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
});
|
});
|
||||||
expect(configs.errorNotification).toEqual({
|
|
||||||
enabled: false,
|
|
||||||
message: "The backport to `{{target-branch}}` failed. Check the latest run for more details."
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("override folder", async () => {
|
test("override folder", async () => {
|
||||||
|
@ -186,9 +182,9 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 4444, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 4444, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), ["0404fb922ab75c3a8aecad5c97d9af388df04695", "11da4e38aa3e577ffde6d546f1c52e53b04d3151"]);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
expect(configs.dryRun).toEqual(true);
|
expect(configs.dryRun).toEqual(true);
|
||||||
expect(configs.auth).toEqual("whatever");
|
expect(configs.auth).toEqual("whatever");
|
||||||
|
@ -221,7 +217,8 @@ describe("github pull request config parser", () => {
|
||||||
},
|
},
|
||||||
bpBranchName: undefined,
|
bpBranchName: undefined,
|
||||||
nCommits: 2,
|
nCommits: 2,
|
||||||
commits: ["0404fb922ab75c3a8aecad5c97d9af388df04695", "11da4e38aa3e577ffde6d546f1c52e53b04d3151"],
|
// taken from head.sha
|
||||||
|
commits: ["91748965051fae1330ad58d15cf694e103267c87"]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -261,7 +258,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -334,7 +331,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -375,7 +372,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -447,7 +444,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -521,7 +518,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -794,7 +791,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -891,7 +888,7 @@ describe("github pull request config parser", () => {
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, true);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
||||||
|
|
||||||
|
@ -943,26 +940,4 @@ describe("github pull request config parser", () => {
|
||||||
comments: ["First comment", "Second comment"],
|
comments: ["First comment", "Second comment"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("enable error notification message", async () => {
|
|
||||||
const args: Args = {
|
|
||||||
dryRun: false,
|
|
||||||
auth: "",
|
|
||||||
pullRequest: mergedPRUrl,
|
|
||||||
targetBranch: "prod",
|
|
||||||
enableErrorNotification: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
|
||||||
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitHubClient.prototype.getPullRequest).toBeCalledWith("owner", "reponame", 2368, undefined);
|
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitHubMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
|
||||||
|
|
||||||
expect(configs.errorNotification).toEqual({
|
|
||||||
"enabled": true,
|
|
||||||
"message": "The backport to `{{target-branch}}` failed. Check the latest run for more details."
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -51,7 +51,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: [],
|
comments: [],
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -124,7 +123,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: [],
|
comments: [],
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -197,8 +195,7 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: [],
|
comments: [],
|
||||||
bpBranchName: "custom-branch",
|
bpBranchName: "custom-branch"
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -271,8 +268,7 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: [],
|
comments: [],
|
||||||
bpBranchName: "custom1, custom2, custom3",
|
bpBranchName: "custom1, custom2, custom3"
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
|
|
@ -88,7 +88,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
inheritReviewers: true,
|
inheritReviewers: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -144,10 +143,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
comments: [],
|
comments: [],
|
||||||
});
|
});
|
||||||
expect(configs.errorNotification).toEqual({
|
|
||||||
"enabled": false,
|
|
||||||
"message": "The backport to `{{target-branch}}` failed. Check the latest run for more details."
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -163,7 +158,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
inheritReviewers: true,
|
inheritReviewers: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -193,7 +187,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
inheritReviewers: true,
|
inheritReviewers: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -250,7 +243,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
inheritReviewers: true,
|
inheritReviewers: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
await expect(() => configParser.parseAndValidate(args)).rejects.toThrow("Provided pull request is closed and not merged");
|
await expect(() => configParser.parseAndValidate(args)).rejects.toThrow("Provided pull request is closed and not merged");
|
||||||
|
@ -270,7 +262,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: [],
|
assignees: [],
|
||||||
inheritReviewers: true,
|
inheritReviewers: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -342,7 +333,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: ["user1", "user2"],
|
reviewers: ["user1", "user2"],
|
||||||
assignees: ["user3", "user4"],
|
assignees: ["user3", "user4"],
|
||||||
inheritReviewers: true, // not taken into account
|
inheritReviewers: true, // not taken into account
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -414,7 +404,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
reviewers: [],
|
reviewers: [],
|
||||||
assignees: ["user3", "user4"],
|
assignees: ["user3", "user4"],
|
||||||
inheritReviewers: false,
|
inheritReviewers: false,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -488,7 +477,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
inheritReviewers: false,
|
inheritReviewers: false,
|
||||||
labels: ["custom-label", "backport-prod"], // also include the one inherited
|
labels: ["custom-label", "backport-prod"], // also include the one inherited
|
||||||
inheritLabels: true,
|
inheritLabels: true,
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -754,7 +742,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: ["First comment", "Second comment"],
|
comments: ["First comment", "Second comment"],
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -829,7 +816,6 @@ describe("gitlab merge request config parser", () => {
|
||||||
labels: [],
|
labels: [],
|
||||||
inheritLabels: false,
|
inheritLabels: false,
|
||||||
comments: ["First comment", "Second comment"],
|
comments: ["First comment", "Second comment"],
|
||||||
squash: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
const configs: Configs = await configParser.parseAndValidate(args);
|
||||||
|
@ -886,26 +872,4 @@ describe("gitlab merge request config parser", () => {
|
||||||
comments: ["First comment", "Second comment"],
|
comments: ["First comment", "Second comment"],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("enable error notification message", async () => {
|
|
||||||
const args: Args = {
|
|
||||||
dryRun: false,
|
|
||||||
auth: "",
|
|
||||||
pullRequest: mergedPRUrl,
|
|
||||||
targetBranch: "prod",
|
|
||||||
enableErrorNotification: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
const configs: Configs = await configParser.parseAndValidate(args);
|
|
||||||
|
|
||||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, undefined);
|
|
||||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []);
|
|
||||||
|
|
||||||
expect(configs.errorNotification).toEqual({
|
|
||||||
"enabled": true,
|
|
||||||
"message": "The backport to `{{target-branch}}` failed. Check the latest run for more details.",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import { inferGitApiUrl, inferGitClient, inferSquash } from "@bp/service/git/git-util";
|
import { inferGitApiUrl, inferGitClient } from "@bp/service/git/git-util";
|
||||||
import { GitClientType } from "@bp/service/git/git.types";
|
import { GitClientType } from "@bp/service/git/git.types";
|
||||||
|
|
||||||
describe("check git utilities", () => {
|
describe("check git utilities", () => {
|
||||||
|
@ -54,11 +54,4 @@ describe("check git utilities", () => {
|
||||||
test("check infer codeberg client", ()=> {
|
test("check infer codeberg client", ()=> {
|
||||||
expect(inferGitClient("https://codeberg.org/lampajr/backporting-example/pulls/1")).toStrictEqual(GitClientType.CODEBERG);
|
expect(inferGitClient("https://codeberg.org/lampajr/backporting-example/pulls/1")).toStrictEqual(GitClientType.CODEBERG);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("check inferSquash", ()=> {
|
|
||||||
expect(inferSquash(true, undefined)).toStrictEqual(false);
|
|
||||||
expect(inferSquash(false, "SHA")).toStrictEqual(true);
|
|
||||||
expect(inferSquash(false, undefined)).toStrictEqual(false);
|
|
||||||
expect(inferSquash(false, null)).toStrictEqual(false);
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -22,7 +22,7 @@ describe("github service", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get pull request: success", async () => {
|
test("get pull request: success", async () => {
|
||||||
const res: GitPullRequest = await gitClient.getPullRequest(TARGET_OWNER, REPO, MERGED_PR_FIXTURE.number, true);
|
const res: GitPullRequest = await gitClient.getPullRequest(TARGET_OWNER, REPO, MERGED_PR_FIXTURE.number);
|
||||||
expect(res.sourceRepo).toEqual({
|
expect(res.sourceRepo).toEqual({
|
||||||
owner: "fork",
|
owner: "fork",
|
||||||
project: "reponame",
|
project: "reponame",
|
||||||
|
|
|
@ -31,7 +31,7 @@ describe("github service", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get merged pull request", async () => {
|
test("get merged pull request", async () => {
|
||||||
const res: GitPullRequest = await gitClient.getPullRequest("superuser", "backporting-example", 1, true);
|
const res: GitPullRequest = await gitClient.getPullRequest("superuser", "backporting-example", 1);
|
||||||
|
|
||||||
// check content
|
// check content
|
||||||
expect(res.sourceRepo).toEqual({
|
expect(res.sourceRepo).toEqual({
|
||||||
|
@ -56,7 +56,7 @@ describe("github service", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get open pull request", async () => {
|
test("get open pull request", async () => {
|
||||||
const res: GitPullRequest = await gitClient.getPullRequest("superuser", "backporting-example", 2, true);
|
const res: GitPullRequest = await gitClient.getPullRequest("superuser", "backporting-example", 2);
|
||||||
expect(res.sourceRepo).toEqual({
|
expect(res.sourceRepo).toEqual({
|
||||||
owner: "superuser",
|
owner: "superuser",
|
||||||
project: "backporting-example",
|
project: "backporting-example",
|
||||||
|
@ -325,7 +325,7 @@ describe("github service", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("get pull request for nested namespaces", async () => {
|
test("get pull request for nested namespaces", async () => {
|
||||||
const res: GitPullRequest = await gitClient.getPullRequestFromUrl("https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4", true);
|
const res: GitPullRequest = await gitClient.getPullRequestFromUrl("https://my.gitlab.host.com/mysuperorg/6/mysuperproduct/mysuperunit/backporting-example/-/merge_requests/4");
|
||||||
|
|
||||||
// check content
|
// check content
|
||||||
expect(res.sourceRepo).toEqual({
|
expect(res.sourceRepo).toEqual({
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,6 @@ const GITHUB_MERGED_PR_W_OVERRIDES_CONFIG_FILE_CONTENT = {
|
||||||
|
|
||||||
jest.mock("@bp/service/git/git-cli");
|
jest.mock("@bp/service/git/git-cli");
|
||||||
jest.spyOn(GitHubClient.prototype, "createPullRequest");
|
jest.spyOn(GitHubClient.prototype, "createPullRequest");
|
||||||
jest.spyOn(GitHubClient.prototype, "createPullRequestComment");
|
|
||||||
jest.spyOn(GitClientFactory, "getOrCreate");
|
jest.spyOn(GitClientFactory, "getOrCreate");
|
||||||
|
|
||||||
let parser: ArgsParser;
|
let parser: ArgsParser;
|
||||||
|
@ -95,7 +94,6 @@ describe("cli runner", () => {
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(0);
|
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(0);
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("overriding author", async () => {
|
test("overriding author", async () => {
|
||||||
|
@ -289,7 +287,6 @@ describe("cli runner", () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("closed and not merged pull request", async () => {
|
test("closed and not merged pull request", async () => {
|
||||||
|
@ -303,7 +300,7 @@ describe("cli runner", () => {
|
||||||
await expect(() => runner.execute()).rejects.toThrow("Provided pull request is closed and not merged");
|
await expect(() => runner.execute()).rejects.toThrow("Provided pull request is closed and not merged");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("open pull request simple", async () => {
|
test("open pull request", async () => {
|
||||||
addProcessArgs([
|
addProcessArgs([
|
||||||
"-tb",
|
"-tb",
|
||||||
"target",
|
"target",
|
||||||
|
@ -350,55 +347,6 @@ describe("cli runner", () => {
|
||||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("open pull request with --auto-no-squash", async () => {
|
|
||||||
addProcessArgs([
|
|
||||||
"-tb",
|
|
||||||
"target",
|
|
||||||
"-pr",
|
|
||||||
"https://github.com/owner/reponame/pull/4444",
|
|
||||||
"--auto-no-squash",
|
|
||||||
]);
|
|
||||||
|
|
||||||
await runner.execute();
|
|
||||||
|
|
||||||
const cwd = process.cwd() + "/bp";
|
|
||||||
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
|
||||||
|
|
||||||
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-0404fb9-11da4e3");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/4444/head:pr/4444");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", undefined, undefined, undefined);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", undefined, undefined, undefined);
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
|
||||||
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "owner",
|
|
||||||
repo: "reponame",
|
|
||||||
head: "bp-target-0404fb9-11da4e3",
|
|
||||||
base: "target",
|
|
||||||
title: "[target] PR Title",
|
|
||||||
body: "**Backport:** https://github.com/owner/reponame/pull/4444\r\n\r\nPlease review and merge",
|
|
||||||
reviewers: ["gh-user"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toReturnTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("override backporting pr data", async () => {
|
test("override backporting pr data", async () => {
|
||||||
addProcessArgs([
|
addProcessArgs([
|
||||||
"-tb",
|
"-tb",
|
||||||
|
@ -733,8 +681,8 @@ describe("cli runner", () => {
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", undefined, undefined, undefined);
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", undefined, undefined, undefined);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
||||||
|
@ -839,8 +787,8 @@ describe("cli runner", () => {
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", "ort", "find-renames", undefined);
|
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", "ort", "find-renames", undefined);
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", "ort", "find-renames", undefined);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", "ort", "find-renames", undefined);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
||||||
|
@ -1159,7 +1107,6 @@ describe("cli runner", () => {
|
||||||
comments: [],
|
comments: [],
|
||||||
});
|
});
|
||||||
expect(GitHubClient.prototype.createPullRequest).toThrowError();
|
expect(GitHubClient.prototype.createPullRequest).toThrowError();
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("auth using GITHUB_TOKEN takes precedence over GIT_TOKEN env variable", async () => {
|
test("auth using GITHUB_TOKEN takes precedence over GIT_TOKEN env variable", async () => {
|
||||||
|
@ -1235,142 +1182,4 @@ describe("cli runner", () => {
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
|
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "prod");
|
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "prod");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("with multiple target branches, one failure and error notification enabled", async () => {
|
|
||||||
jest.spyOn(GitHubClient.prototype, "createPullRequest").mockImplementation((backport: BackportPullRequest) => {
|
|
||||||
throw new Error(`Mocked error: ${backport.base}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
addProcessArgs([
|
|
||||||
"-tb",
|
|
||||||
"v1, v2, v3",
|
|
||||||
"-pr",
|
|
||||||
"https://github.com/owner/reponame/pull/2368",
|
|
||||||
"-f",
|
|
||||||
"/tmp/folder",
|
|
||||||
"--bp-branch-name",
|
|
||||||
"custom-failure-head",
|
|
||||||
"--enable-err-notification",
|
|
||||||
]);
|
|
||||||
|
|
||||||
await expect(() => runner.execute()).rejects.toThrowError("Failure occurred during one of the backports: [Error: Mocked error: v1 ; Error: Mocked error: v2 ; Error: Mocked error: v3]");
|
|
||||||
|
|
||||||
const cwd = "/tmp/folder";
|
|
||||||
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v1");
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v2");
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v3");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined, undefined);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined, undefined);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc", undefined, undefined, undefined);
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v1");
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v2");
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "custom-failure-head-v3");
|
|
||||||
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(3);
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "owner",
|
|
||||||
repo: "reponame",
|
|
||||||
head: "custom-failure-head-v1",
|
|
||||||
base: "v1",
|
|
||||||
title: "[v1] PR Title",
|
|
||||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
|
||||||
reviewers: ["gh-user", "that-s-a-user"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
});
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "owner",
|
|
||||||
repo: "reponame",
|
|
||||||
head: "custom-failure-head-v2",
|
|
||||||
base: "v2",
|
|
||||||
title: "[v2] PR Title",
|
|
||||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
|
||||||
reviewers: ["gh-user", "that-s-a-user"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
});
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "owner",
|
|
||||||
repo: "reponame",
|
|
||||||
head: "custom-failure-head-v3",
|
|
||||||
base: "v3",
|
|
||||||
title: "[v3] PR Title",
|
|
||||||
body: "**Backport:** https://github.com/owner/reponame/pull/2368\r\n\r\nPlease review and merge",
|
|
||||||
reviewers: ["gh-user", "that-s-a-user"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
});
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toThrowError();
|
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(3);
|
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledWith("https://api.github.com/repos/owner/reponame/pulls/2368", "The backport to `v1` failed. Check the latest run for more details.");
|
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledWith("https://api.github.com/repos/owner/reponame/pulls/2368", "The backport to `v2` failed. Check the latest run for more details.");
|
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledWith("https://api.github.com/repos/owner/reponame/pulls/2368", "The backport to `v3` failed. Check the latest run for more details.");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("with some failures and dry run enabled", async () => {
|
|
||||||
jest.spyOn(GitCLIService.prototype, "cherryPick").mockImplementation((cwd: string, sha: string) => {
|
|
||||||
throw new Error(`Forced error: ${sha}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
addProcessArgs([
|
|
||||||
"-tb",
|
|
||||||
"v1, v2, v3",
|
|
||||||
"-pr",
|
|
||||||
"https://github.com/owner/reponame/pull/2368",
|
|
||||||
"-f",
|
|
||||||
"/tmp/folder",
|
|
||||||
"--bp-branch-name",
|
|
||||||
"custom-failure-head",
|
|
||||||
"--enable-err-notification",
|
|
||||||
"--dry-run",
|
|
||||||
]);
|
|
||||||
|
|
||||||
await expect(() => runner.execute()).rejects.toThrowError("Failure occurred during one of the backports: [Error: Forced error: 28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc ; Error: Forced error: 28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc ; Error: Forced error: 28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc]");
|
|
||||||
|
|
||||||
const cwd = "/tmp/folder";
|
|
||||||
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITHUB, undefined, "https://api.github.com");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v1");
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v2");
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://github.com/owner/reponame.git", cwd, "v3");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v1");
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v2");
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "custom-failure-head-v3");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "pull/2368/head:pr/2368");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(3);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toThrowError();
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
|
||||||
|
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(0);
|
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -44,7 +44,6 @@ jest.mock("axios", () => {
|
||||||
|
|
||||||
jest.mock("@bp/service/git/git-cli");
|
jest.mock("@bp/service/git/git-cli");
|
||||||
jest.spyOn(GitLabClient.prototype, "createPullRequest");
|
jest.spyOn(GitLabClient.prototype, "createPullRequest");
|
||||||
jest.spyOn(GitLabClient.prototype, "createPullRequestComment");
|
|
||||||
jest.spyOn(GitClientFactory, "getOrCreate");
|
jest.spyOn(GitClientFactory, "getOrCreate");
|
||||||
|
|
||||||
|
|
||||||
|
@ -106,7 +105,6 @@ describe("cli runner", () => {
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(0);
|
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(0);
|
||||||
expect(GitLabClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("dry run with relative folder", async () => {
|
test("dry run with relative folder", async () => {
|
||||||
|
@ -201,7 +199,6 @@ describe("cli runner", () => {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await expect(() => runner.execute()).rejects.toThrow("Provided pull request is closed and not merged");
|
await expect(() => runner.execute()).rejects.toThrow("Provided pull request is closed and not merged");
|
||||||
expect(GitLabClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("merged pull request", async () => {
|
test("merged pull request", async () => {
|
||||||
|
@ -249,7 +246,6 @@ describe("cli runner", () => {
|
||||||
comments: [],
|
comments: [],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
expect(GitLabClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -582,8 +578,8 @@ describe("cli runner", () => {
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "merge-requests/2/head:pr/2");
|
expect(GitCLIService.prototype.fetch).toBeCalledWith(cwd, "merge-requests/2/head:pr/2");
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenNthCalledWith(1, cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined, undefined);
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenNthCalledWith(2, cwd, "974519f65c9e0ed65277cd71026657a09fca05e7", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "974519f65c9e0ed65277cd71026657a09fca05e7", undefined, undefined, undefined);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336-974519f");
|
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336-974519f");
|
||||||
|
@ -604,50 +600,6 @@ describe("cli runner", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("merged MR with --auto-no-squash", async () => {
|
|
||||||
addProcessArgs([
|
|
||||||
"-tb",
|
|
||||||
"target",
|
|
||||||
"-pr",
|
|
||||||
"https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/5",
|
|
||||||
"--auto-no-squash",
|
|
||||||
]);
|
|
||||||
|
|
||||||
await runner.execute();
|
|
||||||
|
|
||||||
const cwd = process.cwd() + "/bp";
|
|
||||||
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITLAB, undefined, "https://my.gitlab.host.com/api/v4");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://my.gitlab.host.com/superuser/backporting-example.git", cwd, "target");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-e4dd336");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined, undefined);
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336");
|
|
||||||
|
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "superuser",
|
|
||||||
repo: "backporting-example",
|
|
||||||
head: "bp-target-e4dd336",
|
|
||||||
base: "target",
|
|
||||||
title: "[target] Update test.txt",
|
|
||||||
body: expect.stringContaining("**Backport:** https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/5"),
|
|
||||||
reviewers: ["superuser"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("auth using GITLAB_TOKEN takes precedence over GIT_TOKEN env variable", async () => {
|
test("auth using GITLAB_TOKEN takes precedence over GIT_TOKEN env variable", async () => {
|
||||||
process.env[AuthTokenId.GIT_TOKEN] = "mygittoken";
|
process.env[AuthTokenId.GIT_TOKEN] = "mygittoken";
|
||||||
process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken";
|
process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken";
|
||||||
|
|
|
@ -30,7 +30,6 @@ const GITHUB_MERGED_PR_W_OVERRIDES_CONFIG_FILE_CONTENT = {
|
||||||
|
|
||||||
jest.mock("@bp/service/git/git-cli");
|
jest.mock("@bp/service/git/git-cli");
|
||||||
jest.spyOn(GitHubClient.prototype, "createPullRequest");
|
jest.spyOn(GitHubClient.prototype, "createPullRequest");
|
||||||
jest.spyOn(GitHubClient.prototype, "createPullRequestComment");
|
|
||||||
jest.spyOn(GitClientFactory, "getOrCreate");
|
jest.spyOn(GitClientFactory, "getOrCreate");
|
||||||
|
|
||||||
let parser: ArgsParser;
|
let parser: ArgsParser;
|
||||||
|
@ -88,7 +87,6 @@ describe("gha runner", () => {
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
||||||
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(0);
|
expect(GitHubClient.prototype.createPullRequest).toBeCalledTimes(0);
|
||||||
expect(GitHubClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("without dry run", async () => {
|
test("without dry run", async () => {
|
||||||
|
@ -500,8 +498,8 @@ describe("gha runner", () => {
|
||||||
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.fetch).toBeCalledTimes(0);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(2);
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "0404fb922ab75c3a8aecad5c97d9af388df04695", undefined, undefined, undefined);
|
||||||
expect(GitCLIService.prototype.cherryPick).toHaveBeenLastCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", undefined, undefined, undefined);
|
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "11da4e38aa3e577ffde6d546f1c52e53b04d3151", undefined, undefined, undefined);
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-0404fb9-11da4e3");
|
||||||
|
|
|
@ -43,7 +43,6 @@ jest.mock("axios", () => {
|
||||||
|
|
||||||
jest.mock("@bp/service/git/git-cli");
|
jest.mock("@bp/service/git/git-cli");
|
||||||
jest.spyOn(GitLabClient.prototype, "createPullRequest");
|
jest.spyOn(GitLabClient.prototype, "createPullRequest");
|
||||||
jest.spyOn(GitLabClient.prototype, "createPullRequestComment");
|
|
||||||
jest.spyOn(GitClientFactory, "getOrCreate");
|
jest.spyOn(GitClientFactory, "getOrCreate");
|
||||||
|
|
||||||
let parser: ArgsParser;
|
let parser: ArgsParser;
|
||||||
|
@ -99,7 +98,6 @@ describe("gha runner", () => {
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
expect(GitCLIService.prototype.push).toBeCalledTimes(0);
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(0);
|
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(0);
|
||||||
expect(GitLabClient.prototype.createPullRequestComment).toBeCalledTimes(0);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("without dry run", async () => {
|
test("without dry run", async () => {
|
||||||
|
@ -519,46 +517,4 @@ describe("gha runner", () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("merged MR with auto-no-squash", async () => {
|
|
||||||
spyGetInput({
|
|
||||||
"target-branch": "target",
|
|
||||||
"pull-request": "https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/5",
|
|
||||||
"auto-no-squash": "true",
|
|
||||||
});
|
|
||||||
|
|
||||||
await runner.execute();
|
|
||||||
|
|
||||||
const cwd = process.cwd() + "/bp";
|
|
||||||
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledTimes(1);
|
|
||||||
expect(GitClientFactory.getOrCreate).toBeCalledWith(GitClientType.GITLAB, undefined, "https://my.gitlab.host.com/api/v4");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.clone).toBeCalledWith("https://my.gitlab.host.com/superuser/backporting-example.git", cwd, "target");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.createLocalBranch).toBeCalledWith(cwd, "bp-target-e4dd336");
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.cherryPick).toBeCalledWith(cwd, "e4dd336a4a20f394df6665994df382fb1d193a11", undefined, undefined, undefined);
|
|
||||||
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledTimes(1);
|
|
||||||
expect(GitCLIService.prototype.push).toBeCalledWith(cwd, "bp-target-e4dd336");
|
|
||||||
|
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledTimes(1);
|
|
||||||
expect(GitLabClient.prototype.createPullRequest).toBeCalledWith({
|
|
||||||
owner: "superuser",
|
|
||||||
repo: "backporting-example",
|
|
||||||
head: "bp-target-e4dd336",
|
|
||||||
base: "target",
|
|
||||||
title: "[target] Update test.txt",
|
|
||||||
body: expect.stringContaining("**Backport:** https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/5"),
|
|
||||||
reviewers: ["superuser"],
|
|
||||||
assignees: [],
|
|
||||||
labels: [],
|
|
||||||
comments: [],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
});
|
|
@ -1,19 +0,0 @@
|
||||||
import { injectError, injectTargetBranch } from "@bp/service/runner/runner-util";
|
|
||||||
|
|
||||||
describe("check runner utilities", () => {
|
|
||||||
test("properly inject error message", () => {
|
|
||||||
expect(injectError("Original message: {{error}}", "to inject")).toStrictEqual("Original message: to inject");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("missing error placeholder in the original message", () => {
|
|
||||||
expect(injectError("Original message: {{wrong}}", "to inject")).toStrictEqual("Original message: {{wrong}}");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("properly inject target branch into message", () => {
|
|
||||||
expect(injectTargetBranch("Original message: {{target-branch}}", "to inject")).toStrictEqual("Original message: to inject");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("missing target branch placeholder in the original message", () => {
|
|
||||||
expect(injectTargetBranch("Original message: {{wrong}}", "to inject")).toStrictEqual("Original message: {{wrong}}");
|
|
||||||
});
|
|
||||||
});
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,8 +1,7 @@
|
||||||
import LoggerServiceFactory from "@bp/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 { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER, GITHUB_GET_COMMIT } from "./github-data";
|
import { TARGET_OWNER, REPO, MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, NOT_FOUND_PR_NUMBER, MULT_COMMITS_PR_FIXTURE, MULT_COMMITS_PR_COMMITS, NEW_PR_URL, NEW_PR_NUMBER } from "./github-data";
|
||||||
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER, MERGED_SQUASHED_MR_COMMITS, MERGED_NOT_SQUASHED_MR, MERGED_NOT_SQUASHED_MR_COMMITS } from "./gitlab-data";
|
import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, NESTED_NAMESPACE_MR, OPEN_MR, OPEN_PR_COMMITS, PROJECT_EXAMPLE, NESTED_PROJECT_EXAMPLE, SUPERUSER, MERGED_SQUASHED_MR_COMMITS } from "./gitlab-data";
|
||||||
import { CB_TARGET_OWNER, CB_REPO, CB_MERGED_PR_FIXTURE, CB_OPEN_PR_FIXTURE, CB_NOT_MERGED_PR_FIXTURE, CB_NOT_FOUND_PR_NUMBER, CB_MULT_COMMITS_PR_FIXTURE, CB_MULT_COMMITS_PR_COMMITS, CB_NEW_PR_URL, CB_NEW_PR_NUMBER, CODEBERG_GET_COMMIT } from "./codeberg-data";
|
|
||||||
|
|
||||||
// high number, for each test we are not expecting
|
// high number, for each test we are not expecting
|
||||||
// to send more than 3 reqs per api endpoint
|
// to send more than 3 reqs per api endpoint
|
||||||
|
@ -25,8 +24,6 @@ export const getAxiosMocked = (url: string) => {
|
||||||
data = CLOSED_NOT_MERGED_MR;
|
data = CLOSED_NOT_MERGED_MR;
|
||||||
} else if (url.endsWith("merge_requests/4")) {
|
} else if (url.endsWith("merge_requests/4")) {
|
||||||
data = NESTED_NAMESPACE_MR;
|
data = NESTED_NAMESPACE_MR;
|
||||||
} else if (url.endsWith("merge_requests/5")) {
|
|
||||||
data = MERGED_NOT_SQUASHED_MR;
|
|
||||||
} else if (url.endsWith("projects/76316")) {
|
} else if (url.endsWith("projects/76316")) {
|
||||||
data = PROJECT_EXAMPLE;
|
data = PROJECT_EXAMPLE;
|
||||||
} else if (url.endsWith("projects/1645")) {
|
} else if (url.endsWith("projects/1645")) {
|
||||||
|
@ -37,8 +34,6 @@ export const getAxiosMocked = (url: string) => {
|
||||||
data = MERGED_SQUASHED_MR_COMMITS;
|
data = MERGED_SQUASHED_MR_COMMITS;
|
||||||
} else if (url.endsWith("merge_requests/2/commits")) {
|
} else if (url.endsWith("merge_requests/2/commits")) {
|
||||||
data = OPEN_PR_COMMITS;
|
data = OPEN_PR_COMMITS;
|
||||||
} else if (url.endsWith("merge_requests/5/commits")) {
|
|
||||||
data = MERGED_NOT_SQUASHED_MR_COMMITS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -162,17 +157,6 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||||
data: MULT_COMMITS_PR_COMMITS
|
data: MULT_COMMITS_PR_COMMITS
|
||||||
});
|
});
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.listCommits({
|
|
||||||
owner: TARGET_OWNER,
|
|
||||||
repo: REPO,
|
|
||||||
pull_number: OPEN_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: MULT_COMMITS_PR_COMMITS
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
mock.rest.pulls
|
||||||
.create()
|
.create()
|
||||||
.reply({
|
.reply({
|
||||||
|
@ -216,17 +200,6 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||||
data: {}
|
data: {}
|
||||||
});
|
});
|
||||||
|
|
||||||
mock.rest.git
|
|
||||||
.getCommit({
|
|
||||||
owner: TARGET_OWNER,
|
|
||||||
repo: REPO,
|
|
||||||
commit_sha: "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: GITHUB_GET_COMMIT,
|
|
||||||
});
|
|
||||||
|
|
||||||
// invalid requests
|
// invalid requests
|
||||||
mock.rest.pulls
|
mock.rest.pulls
|
||||||
.get({
|
.get({
|
||||||
|
@ -244,151 +217,3 @@ export const mockGitHubClient = (apiUrl = "https://api.github.com"): Moctokit =>
|
||||||
|
|
||||||
return mock;
|
return mock;
|
||||||
};
|
};
|
||||||
|
|
||||||
// CODEBERG - OCTOKIT
|
|
||||||
|
|
||||||
export const mockCodebergClient = (apiUrl = "https://codeberg.org/api/v1"): Moctokit => {
|
|
||||||
logger.debug("Setting up moctokit..");
|
|
||||||
|
|
||||||
const mock = new Moctokit(apiUrl);
|
|
||||||
|
|
||||||
// setup the mock requests here
|
|
||||||
|
|
||||||
// valid requests
|
|
||||||
mock.rest.pulls
|
|
||||||
.get({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_MERGED_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_MERGED_PR_FIXTURE
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.get({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_MULT_COMMITS_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_MULT_COMMITS_PR_FIXTURE
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.get({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_OPEN_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_OPEN_PR_FIXTURE
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.get({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_NOT_MERGED_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_NOT_MERGED_PR_FIXTURE
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.listCommits({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_MULT_COMMITS_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_MULT_COMMITS_PR_COMMITS
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.listCommits({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_OPEN_PR_FIXTURE.number
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CB_MULT_COMMITS_PR_COMMITS
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.create()
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 201,
|
|
||||||
data: {
|
|
||||||
number: CB_NEW_PR_NUMBER,
|
|
||||||
html_url: CB_NEW_PR_URL,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.pulls
|
|
||||||
.requestReviewers()
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 201,
|
|
||||||
data: CB_MERGED_PR_FIXTURE
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.issues
|
|
||||||
.addAssignees()
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 201,
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.issues
|
|
||||||
.addLabels()
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 200,
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.issues
|
|
||||||
.createComment()
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 201,
|
|
||||||
data: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
mock.rest.git
|
|
||||||
.getCommit({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
commit_sha: "28f63db774185f4ec4b57cd9aaeb12dbfb4c9ecc",
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
status: 200,
|
|
||||||
data: CODEBERG_GET_COMMIT,
|
|
||||||
});
|
|
||||||
|
|
||||||
// invalid requests
|
|
||||||
mock.rest.pulls
|
|
||||||
.get({
|
|
||||||
owner: CB_TARGET_OWNER,
|
|
||||||
repo: CB_REPO,
|
|
||||||
pull_number: CB_NOT_FOUND_PR_NUMBER
|
|
||||||
})
|
|
||||||
.reply({
|
|
||||||
repeat: REPEAT,
|
|
||||||
status: 404,
|
|
||||||
data: {
|
|
||||||
message: "Not found"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return mock;
|
|
||||||
};
|
|
||||||
|
|
|
@ -1832,14 +1832,6 @@ export const MULT_COMMITS_PR_FIXTURE = {
|
||||||
"changed_files": 2
|
"changed_files": 2
|
||||||
};
|
};
|
||||||
|
|
||||||
export const GITHUB_GET_COMMIT = {
|
|
||||||
"parents": [
|
|
||||||
{
|
|
||||||
"sha": "SHA"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
export const MULT_COMMITS_PR_COMMITS = [
|
export const MULT_COMMITS_PR_COMMITS = [
|
||||||
{
|
{
|
||||||
"sha": "0404fb922ab75c3a8aecad5c97d9af388df04695",
|
"sha": "0404fb922ab75c3a8aecad5c97d9af388df04695",
|
||||||
|
|
|
@ -755,29 +755,6 @@ export const OPEN_PR_COMMITS = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
export const MERGED_NOT_SQUASHED_MR_COMMITS = [
|
|
||||||
{
|
|
||||||
"id":"e4dd336a4a20f394df6665994df382fb1d193a11",
|
|
||||||
"short_id":"e4dd336a",
|
|
||||||
"created_at":"2023-06-29T09:59:10.000Z",
|
|
||||||
"parent_ids":[
|
|
||||||
|
|
||||||
],
|
|
||||||
"title":"Add new file",
|
|
||||||
"message":"Add new file",
|
|
||||||
"author_name":"Super User",
|
|
||||||
"author_email":"superuser@email.com",
|
|
||||||
"authored_date":"2023-06-29T09:59:10.000Z",
|
|
||||||
"committer_name":"Super User",
|
|
||||||
"committer_email":"superuser@email.com",
|
|
||||||
"committed_date":"2023-06-29T09:59:10.000Z",
|
|
||||||
"trailers":{
|
|
||||||
|
|
||||||
},
|
|
||||||
"web_url":"https://gitlab.com/superuser/backporting-example/-/commit/e4dd336a4a20f394df6665994df382fb1d193a11"
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const SUPERUSER = {
|
export const SUPERUSER = {
|
||||||
"id":14041,
|
"id":14041,
|
||||||
"username":"superuser",
|
"username":"superuser",
|
||||||
|
@ -921,138 +898,3 @@ export const NESTED_NAMESPACE_MR = {
|
||||||
"can_merge":true
|
"can_merge":true
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MERGED_NOT_SQUASHED_MR = {
|
|
||||||
"id":807106,
|
|
||||||
"iid":1,
|
|
||||||
"project_id":76316,
|
|
||||||
"title":"Update test.txt",
|
|
||||||
"description":"This is the body",
|
|
||||||
"state":"merged",
|
|
||||||
"created_at":"2023-06-28T14:32:40.943Z",
|
|
||||||
"updated_at":"2023-06-28T14:37:12.108Z",
|
|
||||||
"merged_by":{
|
|
||||||
"id":14041,
|
|
||||||
"username":"superuser",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
},
|
|
||||||
"merge_user":{
|
|
||||||
"id":14041,
|
|
||||||
"username":"superuser",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
},
|
|
||||||
"merged_at":"2023-06-28T14:37:11.667Z",
|
|
||||||
"closed_by":null,
|
|
||||||
"closed_at":null,
|
|
||||||
"target_branch":"main",
|
|
||||||
"source_branch":"feature",
|
|
||||||
"user_notes_count":0,
|
|
||||||
"upvotes":0,
|
|
||||||
"downvotes":0,
|
|
||||||
"author":{
|
|
||||||
"id":14041,
|
|
||||||
"username":"superuser",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
},
|
|
||||||
"assignees":[
|
|
||||||
{
|
|
||||||
"id":14041,
|
|
||||||
"username":"superuser",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"assignee":{
|
|
||||||
"id":14041,
|
|
||||||
"username":"superuser",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
},
|
|
||||||
"reviewers":[
|
|
||||||
{
|
|
||||||
"id":1404188,
|
|
||||||
"username":"superuser1",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id":1404199,
|
|
||||||
"username":"superuser2",
|
|
||||||
"name":"Super User",
|
|
||||||
"state":"active",
|
|
||||||
"avatar_url":"https://my.gitlab.host.com/uploads/-/system/user/avatar/14041/avatar.png",
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"source_project_id":76316,
|
|
||||||
"target_project_id":76316,
|
|
||||||
"labels":[
|
|
||||||
"backport-prod"
|
|
||||||
],
|
|
||||||
"draft":false,
|
|
||||||
"work_in_progress":false,
|
|
||||||
"milestone":null,
|
|
||||||
"merge_when_pipeline_succeeds":false,
|
|
||||||
"merge_status":"can_be_merged",
|
|
||||||
"detailed_merge_status":"not_open",
|
|
||||||
"sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
|
|
||||||
"merge_commit_sha":"4d369c3e9a8d1d5b7e56c892a8ab2a7666583ac3",
|
|
||||||
"squash_commit_sha":null,
|
|
||||||
"discussion_locked":null,
|
|
||||||
"should_remove_source_branch":true,
|
|
||||||
"force_remove_source_branch":true,
|
|
||||||
"reference":"!5",
|
|
||||||
"references":{
|
|
||||||
"short":"!5",
|
|
||||||
"relative":"!5",
|
|
||||||
"full":"superuser/backporting-example!5"
|
|
||||||
},
|
|
||||||
"web_url":"https://my.gitlab.host.com/superuser/backporting-example/-/merge_requests/5",
|
|
||||||
"time_stats":{
|
|
||||||
"time_estimate":0,
|
|
||||||
"total_time_spent":0,
|
|
||||||
"human_time_estimate":null,
|
|
||||||
"human_total_time_spent":null
|
|
||||||
},
|
|
||||||
"squash":false,
|
|
||||||
"squash_on_merge":false,
|
|
||||||
"task_completion_status":{
|
|
||||||
"count":0,
|
|
||||||
"completed_count":0
|
|
||||||
},
|
|
||||||
"has_conflicts":false,
|
|
||||||
"blocking_discussions_resolved":true,
|
|
||||||
"approvals_before_merge":null,
|
|
||||||
"subscribed":true,
|
|
||||||
"changes_count":"1",
|
|
||||||
"latest_build_started_at":null,
|
|
||||||
"latest_build_finished_at":null,
|
|
||||||
"first_deployed_to_production_at":null,
|
|
||||||
"pipeline":null,
|
|
||||||
"head_pipeline":null,
|
|
||||||
"diff_refs":{
|
|
||||||
"base_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b",
|
|
||||||
"head_sha":"9e15674ebd48e05c6e428a1fa31dbb60a778d644",
|
|
||||||
"start_sha":"2c553a0c4c133a51806badce5fa4842b7253cb3b"
|
|
||||||
},
|
|
||||||
"merge_error":null,
|
|
||||||
"first_contribution":false,
|
|
||||||
"user":{
|
|
||||||
"can_merge":true
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Add table
Reference in a new issue