From 70da575afce603190eafed927637922a37fbd087 Mon Sep 17 00:00:00 2001 From: Andrea Lamparelli Date: Sun, 10 Dec 2023 22:05:53 +0100 Subject: [PATCH] feat(gh-85): take git tokens from environment (#88) --- README.md | 17 ++- action.yml | 2 +- dist/cli/index.js | 85 ++++++++++++++- dist/gha/index.js | 83 ++++++++++++++- src/service/args/cli/cli-args-parser.ts | 2 +- src/service/configs/configs-parser.ts | 41 ++++++- src/service/configs/configs.types.ts | 10 ++ .../configs/pullrequest/pr-configs-parser.ts | 12 ++- src/service/git/git-client-factory.ts | 2 +- src/service/git/git-client.ts | 7 +- src/service/git/github/github-client.ts | 8 +- src/service/git/gitlab/gitlab-client.ts | 6 +- .../github-pr-configs-parser-multiple.test.ts | 5 +- .../github-pr-configs-parser.test.ts | 88 ++++++++++++++- .../gitlab-pr-configs-parser-multiple.test.ts | 4 + .../gitlab-pr-configs-parser.test.ts | 100 +++++++++++++++++- test/support/utils.ts | 8 ++ 17 files changed, 456 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index fba7fb3..57734a5 100644 --- a/README.md +++ b/README.md @@ -97,7 +97,7 @@ This tool comes with some inputs that allow users to override the default behavi | Target Branches | -tb, --target-branch | N | Comma separated list of branches where the changes must be backported to | | | Pull Request | -pr, --pull-request | N | Original pull request url, the one that must be backported, e.g., https://github.com/kiegroup/git-backporting/pull/1 | | | Configuration File | -cf, --config-file | N | Configuration file, in JSON format, containing all options to be overridded, note that if provided all other CLI options will be ignored | | -| Auth | -a, --auth | N | `GITHUB_TOKEN`, `GITLAB_TOKEN` or a `repo` scoped [Personal Access Token](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) | "" | +| Auth | -a, --auth | N | Git access/authorization token, if provided all token env variables will be ignored. See [auth token](#authorization-token) section for more details | "" | | Folder | -f, --folder | N | Local folder full name of the repository that will be checked out, e.g., /tmp/folder | {cwd}/bp | | Git User | -gu, --git-user | N | Local git user name | "GitHub" | | Git Email | -ge, --git-email | N | Local git user email | "noreply@github.com" | @@ -118,6 +118,17 @@ This tool comes with some inputs that allow users to override the default behavi > **NOTE**: `pull request` and `target branch` are *mandatory*, they must be provided as CLI options or as part of the configuration file (if used). +#### Authorization token + +Since version `4.5.0` we introduced a new feature that allows user to provide the git access token through environment variables. These env variables are taken into consideration only if the `--auth/-a` is not provided as argument/input. +Here the supported list of env variables: +- `GITHUB_TOKEN`: this is checked only if backporting on Github platform. +- `GITLAB_TOKEN`: this is checked only if backporting on Gitlab platform. +- `CODEBERG_TOKEN`: this is checked only if backporting on Codeberg platform. +- `GIT_TOKEN`: this is considered if none of the previous envs are set. + +> **NOTE**: if `--auth` argument is provided, all env variables will be ignored even if not empty. + #### Configuration file example This is an example of a configuration file that can be used. @@ -194,6 +205,9 @@ on: - closed - labeled +env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + jobs: backporting: name: "Backporting" @@ -216,7 +230,6 @@ jobs: with: target-branch: v1 pull-request: ${{ github.event.pull_request.url }} - auth: ${{ secrets.GITHUB_TOKEN }} ``` For a complete description of all inputs see [Inputs section](#inputs). diff --git a/action.yml b/action.yml index 6f69528..aad7087 100644 --- a/action.yml +++ b/action.yml @@ -15,7 +15,7 @@ inputs: required: false default: "false" auth: - description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT)" + description: "GITHUB_TOKEN or a `repo` scoped Personal Access Token (PAT), if not provided will look for existing env variables like GITHUB_TOKEN" default: ${{ github.token }} required: false git-user: diff --git a/dist/cli/index.js b/dist/cli/index.js index d213c76..24e6781 100755 --- a/dist/cli/index.js +++ b/dist/cli/index.js @@ -182,7 +182,7 @@ class CLIArgsParser extends args_parser_1.default { .option("-tb, --target-branch ", "comma separated list of branches where changes must be backported to") .option("-pr, --pull-request ", "pull request url, e.g., https://github.com/kiegroup/git-backporting/pull/1") .option("-d, --dry-run", "if enabled the tool does not create any pull request nor push anything remotely") - .option("-a, --auth ", "git service authentication string, e.g., github token") + .option("-a, --auth ", "git authentication string, if not provided fallback by looking for existing env variables like GITHUB_TOKEN") .option("-gu, --git-user ", "local git user name, default is 'GitHub'") .option("-ge, --git-email ", "local git user email, default is 'noreply@github.com'") .option("-f, --folder ", "local folder where the repo will be checked out, e.g., /tmp/folder") @@ -251,7 +251,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); +const configs_types_1 = __nccwpck_require__(4753); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); +const git_types_1 = __nccwpck_require__(750); /** * Abstract configuration parser class in charge to parse * Args and produces a common Configs object @@ -273,10 +275,68 @@ class ConfigsParser { } return Promise.resolve(configs); } + /** + * Retrieve the git token from env variable, the default is taken from GIT_TOKEN env. + * All specific git env variable have precedence and override the default one. + * @param gitType + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + getGitTokenFromEnv(gitType) { + let [token] = this.getEnv(configs_types_1.AuthTokenId.GIT_TOKEN); + let [specToken, specOk] = [undefined, false]; + if (git_types_1.GitClientType.GITHUB == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITHUB_TOKEN); + } + else if (git_types_1.GitClientType.GITLAB == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITLAB_TOKEN); + } + else if (git_types_1.GitClientType.CODEBERG == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.CODEBERG_TOKEN); + } + if (specOk) { + token = specToken; + } + return token; + } + /** + * Get process env variable given the input key string + * @param key + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + getEnv(key) { + const val = process.env[key]; + return [val, val !== undefined && val !== ""]; + } } exports["default"] = ConfigsParser; +/***/ }), + +/***/ 4753: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.AuthTokenId = void 0; +var AuthTokenId; +(function (AuthTokenId) { + // github specific token + AuthTokenId["GITHUB_TOKEN"] = "GITHUB_TOKEN"; + // gitlab specific token + AuthTokenId["GITLAB_TOKEN"] = "GITLAB_TOKEN"; + // codeberg specific token + AuthTokenId["CODEBERG_TOKEN"] = "CODEBERG_TOKEN"; + // generic git token + AuthTokenId["GIT_TOKEN"] = "GIT_TOKEN"; +})(AuthTokenId = exports.AuthTokenId || (exports.AuthTokenId = {})); + + /***/ }), /***/ 6618: @@ -311,9 +371,18 @@ class PullRequestConfigsParser extends configs_parser_1.default { if (bpBranchNames.length > 1 && bpBranchNames.length != targetBranches.length) { throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`); } + // setup the auth token + let token = args.auth; + if (token === undefined) { + this.logger.info("Auth argument not provided, checking available tokens from env.."); + token = this.getGitTokenFromEnv(this.gitClient.getClientType()); + if (!token) { + this.logger.info("Git token not set in the env"); + } + } return { dryRun: args.dryRun, - auth: args.auth, + auth: token, folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`, mergeStrategy: args.strategy, mergeStrategyOption: args.strategyOption, @@ -567,7 +636,7 @@ class GitClientFactory { GitClientFactory.instance = new gitlab_client_1.default(authToken, apiUrl); break; case git_types_1.GitClientType.CODEBERG: - GitClientFactory.instance = new github_client_1.default(authToken, apiUrl); + GitClientFactory.instance = new github_client_1.default(authToken, apiUrl, true); break; default: throw new Error(`Invalid git service type received: ${type}`); @@ -673,12 +742,16 @@ const github_mapper_1 = __importDefault(__nccwpck_require__(5764)); const octokit_factory_1 = __importDefault(__nccwpck_require__(4257)); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); class GitHubClient { - constructor(token, apiUrl) { + constructor(token, apiUrl, isForCodeberg = false) { this.apiUrl = apiUrl; + this.isForCodeberg = isForCodeberg; this.logger = logger_service_factory_1.default.getLogger(); this.octokit = octokit_factory_1.default.getOctokit(token, this.apiUrl); this.mapper = new github_mapper_1.default(); } + getClientType() { + return this.isForCodeberg ? git_types_1.GitClientType.CODEBERG : git_types_1.GitClientType.GITHUB; + } // READ getDefaultGitUser() { return this.apiUrl.includes(git_types_1.GitClientType.CODEBERG.toString()) ? "Codeberg" : "GitHub"; @@ -889,6 +962,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); +const git_types_1 = __nccwpck_require__(750); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); const gitlab_mapper_1 = __importDefault(__nccwpck_require__(2675)); const axios_1 = __importDefault(__nccwpck_require__(8757)); @@ -909,6 +983,9 @@ class GitLabClient { }); this.mapper = new gitlab_mapper_1.default(this.client); } + getClientType() { + return git_types_1.GitClientType.GITLAB; + } getDefaultGitUser() { return "Gitlab"; } diff --git a/dist/gha/index.js b/dist/gha/index.js index 72b2528..344c82f 100755 --- a/dist/gha/index.js +++ b/dist/gha/index.js @@ -221,7 +221,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); +const configs_types_1 = __nccwpck_require__(4753); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); +const git_types_1 = __nccwpck_require__(750); /** * Abstract configuration parser class in charge to parse * Args and produces a common Configs object @@ -243,10 +245,68 @@ class ConfigsParser { } return Promise.resolve(configs); } + /** + * Retrieve the git token from env variable, the default is taken from GIT_TOKEN env. + * All specific git env variable have precedence and override the default one. + * @param gitType + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + getGitTokenFromEnv(gitType) { + let [token] = this.getEnv(configs_types_1.AuthTokenId.GIT_TOKEN); + let [specToken, specOk] = [undefined, false]; + if (git_types_1.GitClientType.GITHUB == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITHUB_TOKEN); + } + else if (git_types_1.GitClientType.GITLAB == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.GITLAB_TOKEN); + } + else if (git_types_1.GitClientType.CODEBERG == gitType) { + [specToken, specOk] = this.getEnv(configs_types_1.AuthTokenId.CODEBERG_TOKEN); + } + if (specOk) { + token = specToken; + } + return token; + } + /** + * Get process env variable given the input key string + * @param key + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + getEnv(key) { + const val = process.env[key]; + return [val, val !== undefined && val !== ""]; + } } exports["default"] = ConfigsParser; +/***/ }), + +/***/ 4753: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.AuthTokenId = void 0; +var AuthTokenId; +(function (AuthTokenId) { + // github specific token + AuthTokenId["GITHUB_TOKEN"] = "GITHUB_TOKEN"; + // gitlab specific token + AuthTokenId["GITLAB_TOKEN"] = "GITLAB_TOKEN"; + // codeberg specific token + AuthTokenId["CODEBERG_TOKEN"] = "CODEBERG_TOKEN"; + // generic git token + AuthTokenId["GIT_TOKEN"] = "GIT_TOKEN"; +})(AuthTokenId = exports.AuthTokenId || (exports.AuthTokenId = {})); + + /***/ }), /***/ 6618: @@ -281,9 +341,18 @@ class PullRequestConfigsParser extends configs_parser_1.default { if (bpBranchNames.length > 1 && bpBranchNames.length != targetBranches.length) { throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`); } + // setup the auth token + let token = args.auth; + if (token === undefined) { + this.logger.info("Auth argument not provided, checking available tokens from env.."); + token = this.getGitTokenFromEnv(this.gitClient.getClientType()); + if (!token) { + this.logger.info("Git token not set in the env"); + } + } return { dryRun: args.dryRun, - auth: args.auth, + auth: token, folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`, mergeStrategy: args.strategy, mergeStrategyOption: args.strategyOption, @@ -537,7 +606,7 @@ class GitClientFactory { GitClientFactory.instance = new gitlab_client_1.default(authToken, apiUrl); break; case git_types_1.GitClientType.CODEBERG: - GitClientFactory.instance = new github_client_1.default(authToken, apiUrl); + GitClientFactory.instance = new github_client_1.default(authToken, apiUrl, true); break; default: throw new Error(`Invalid git service type received: ${type}`); @@ -643,12 +712,16 @@ const github_mapper_1 = __importDefault(__nccwpck_require__(5764)); const octokit_factory_1 = __importDefault(__nccwpck_require__(4257)); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); class GitHubClient { - constructor(token, apiUrl) { + constructor(token, apiUrl, isForCodeberg = false) { this.apiUrl = apiUrl; + this.isForCodeberg = isForCodeberg; this.logger = logger_service_factory_1.default.getLogger(); this.octokit = octokit_factory_1.default.getOctokit(token, this.apiUrl); this.mapper = new github_mapper_1.default(); } + getClientType() { + return this.isForCodeberg ? git_types_1.GitClientType.CODEBERG : git_types_1.GitClientType.GITHUB; + } // READ getDefaultGitUser() { return this.apiUrl.includes(git_types_1.GitClientType.CODEBERG.toString()) ? "Codeberg" : "GitHub"; @@ -859,6 +932,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", ({ value: true })); +const git_types_1 = __nccwpck_require__(750); const logger_service_factory_1 = __importDefault(__nccwpck_require__(8936)); const gitlab_mapper_1 = __importDefault(__nccwpck_require__(2675)); const axios_1 = __importDefault(__nccwpck_require__(8757)); @@ -879,6 +953,9 @@ class GitLabClient { }); this.mapper = new gitlab_mapper_1.default(this.client); } + getClientType() { + return git_types_1.GitClientType.GITLAB; + } getDefaultGitUser() { return "Gitlab"; } diff --git a/src/service/args/cli/cli-args-parser.ts b/src/service/args/cli/cli-args-parser.ts index 9a9b348..c8ebc92 100644 --- a/src/service/args/cli/cli-args-parser.ts +++ b/src/service/args/cli/cli-args-parser.ts @@ -13,7 +13,7 @@ export default class CLIArgsParser extends ArgsParser { .option("-tb, --target-branch ", "comma separated list of branches where changes must be backported to") .option("-pr, --pull-request ", "pull request url, e.g., https://github.com/kiegroup/git-backporting/pull/1") .option("-d, --dry-run", "if enabled the tool does not create any pull request nor push anything remotely") - .option("-a, --auth ", "git service authentication string, e.g., github token") + .option("-a, --auth ", "git authentication string, if not provided fallback by looking for existing env variables like GITHUB_TOKEN") .option("-gu, --git-user ", "local git user name, default is 'GitHub'") .option("-ge, --git-email ", "local git user email, default is 'noreply@github.com'") .option("-f, --folder ", "local folder where the repo will be checked out, e.g., /tmp/folder") diff --git a/src/service/configs/configs-parser.ts b/src/service/configs/configs-parser.ts index 5b901cb..379e39b 100644 --- a/src/service/configs/configs-parser.ts +++ b/src/service/configs/configs-parser.ts @@ -1,7 +1,8 @@ import { Args } from "@bp/service/args/args.types"; -import { Configs } from "@bp/service/configs/configs.types"; +import { AuthTokenId, Configs } from "@bp/service/configs/configs.types"; import LoggerService from "../logger/logger-service"; import LoggerServiceFactory from "../logger/logger-service-factory"; +import { GitClientType } from "../git/git.types"; /** * Abstract configuration parser class in charge to parse @@ -34,4 +35,42 @@ import LoggerServiceFactory from "../logger/logger-service-factory"; return Promise.resolve(configs); } + + /** + * Retrieve the git token from env variable, the default is taken from GIT_TOKEN env. + * All specific git env variable have precedence and override the default one. + * @param gitType + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + public getGitTokenFromEnv(gitType: GitClientType): string | undefined { + let [token] = this.getEnv(AuthTokenId.GIT_TOKEN); + let [specToken, specOk]: [string | undefined, boolean] = [undefined, false]; + if (GitClientType.GITHUB == gitType) { + [specToken, specOk] = this.getEnv(AuthTokenId.GITHUB_TOKEN); + } else if (GitClientType.GITLAB == gitType) { + [specToken, specOk] = this.getEnv(AuthTokenId.GITLAB_TOKEN); + } else if (GitClientType.CODEBERG == gitType) { + [specToken, specOk] = this.getEnv(AuthTokenId.CODEBERG_TOKEN); + } + + if (specOk) { + token = specToken; + } + + return token; + } + + /** + * Get process env variable given the input key string + * @param key + * @returns tuple where + * - the first element is the corresponding env value + * - the second element is true if the value is not undefined nor empty + */ + public getEnv(key: string): [string | undefined, boolean] { + const val = process.env[key]; + return [val, val !== undefined && val !== ""]; + } } \ No newline at end of file diff --git a/src/service/configs/configs.types.ts b/src/service/configs/configs.types.ts index fa44e51..b7ed0e8 100644 --- a/src/service/configs/configs.types.ts +++ b/src/service/configs/configs.types.ts @@ -21,3 +21,13 @@ export interface Configs { backportPullRequests: BackportPullRequest[], } +export enum AuthTokenId { + // github specific token + GITHUB_TOKEN = "GITHUB_TOKEN", + // gitlab specific token + GITLAB_TOKEN = "GITLAB_TOKEN", + // codeberg specific token + CODEBERG_TOKEN = "CODEBERG_TOKEN", + // generic git token + GIT_TOKEN = "GIT_TOKEN", +} \ No newline at end of file diff --git a/src/service/configs/pullrequest/pr-configs-parser.ts b/src/service/configs/pullrequest/pr-configs-parser.ts index 00e220e..34c3c88 100644 --- a/src/service/configs/pullrequest/pr-configs-parser.ts +++ b/src/service/configs/pullrequest/pr-configs-parser.ts @@ -33,9 +33,19 @@ export default class PullRequestConfigsParser extends ConfigsParser { throw new Error(`The number of backport branch names, if provided, must match the number of target branches or just one, provided ${bpBranchNames.length} branch names instead`); } + // setup the auth token + let token = args.auth; + if (token === undefined) { + this.logger.info("Auth argument not provided, checking available tokens from env.."); + token = this.getGitTokenFromEnv(this.gitClient.getClientType()); + if (!token) { + this.logger.info("Git token not set in the env"); + } + } + return { dryRun: args.dryRun!, - auth: args.auth, + auth: token, folder: `${folder.startsWith("/") ? "" : process.cwd() + "/"}${args.folder ?? this.getDefaultFolder()}`, mergeStrategy: args.strategy, mergeStrategyOption: args.strategyOption, diff --git a/src/service/git/git-client-factory.ts b/src/service/git/git-client-factory.ts index ed66a75..7b38308 100644 --- a/src/service/git/git-client-factory.ts +++ b/src/service/git/git-client-factory.ts @@ -44,7 +44,7 @@ export default class GitClientFactory { GitClientFactory.instance = new GitLabClient(authToken, apiUrl); break; case GitClientType.CODEBERG: - GitClientFactory.instance = new GitHubService(authToken, apiUrl); + GitClientFactory.instance = new GitHubService(authToken, apiUrl, true); break; default: throw new Error(`Invalid git service type received: ${type}`); diff --git a/src/service/git/git-client.ts b/src/service/git/git-client.ts index 7b98c1b..c9d0f10 100644 --- a/src/service/git/git-client.ts +++ b/src/service/git/git-client.ts @@ -1,4 +1,4 @@ -import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types"; +import { BackportPullRequest, GitClientType, GitPullRequest } from "@bp/service/git/git.types"; /** * Git management service interface, which provides a common API for interacting @@ -6,6 +6,11 @@ import { BackportPullRequest, GitPullRequest } from "@bp/service/git/git.types"; */ export default interface GitClient { + /** + * @returns {GitClientType} specific git client enum type + */ + getClientType(): GitClientType + // READ getDefaultGitUser(): string; diff --git a/src/service/git/github/github-client.ts b/src/service/git/github/github-client.ts index f83f3e2..40f1831 100644 --- a/src/service/git/github/github-client.ts +++ b/src/service/git/github/github-client.ts @@ -11,16 +11,22 @@ export default class GitHubClient implements GitClient { private logger: LoggerService; private apiUrl: string; + private isForCodeberg: boolean; private octokit: Octokit; private mapper: GitHubMapper; - constructor(token: string | undefined, apiUrl: string) { + constructor(token: string | undefined, apiUrl: string, isForCodeberg = false) { this.apiUrl = apiUrl; + this.isForCodeberg = isForCodeberg; this.logger = LoggerServiceFactory.getLogger(); this.octokit = OctokitFactory.getOctokit(token, this.apiUrl); this.mapper = new GitHubMapper(); } + getClientType(): GitClientType { + return this.isForCodeberg ? GitClientType.CODEBERG : GitClientType.GITHUB; + } + // READ getDefaultGitUser(): string { diff --git a/src/service/git/gitlab/gitlab-client.ts b/src/service/git/gitlab/gitlab-client.ts index 0d1f962..c874b70 100644 --- a/src/service/git/gitlab/gitlab-client.ts +++ b/src/service/git/gitlab/gitlab-client.ts @@ -1,6 +1,6 @@ import LoggerService from "@bp/service/logger/logger-service"; import GitClient from "@bp/service/git/git-client"; -import { GitPullRequest, BackportPullRequest } from "@bp/service/git/git.types"; +import { GitPullRequest, BackportPullRequest, GitClientType } from "@bp/service/git/git.types"; import LoggerServiceFactory from "@bp/service/logger/logger-service-factory"; import { CommitSchema, MergeRequestSchema, UserSchema } from "@gitbeaker/rest"; import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper"; @@ -30,6 +30,10 @@ export default class GitLabClient implements GitClient { this.mapper = new GitLabMapper(this.client); } + getClientType(): GitClientType { + return GitClientType.GITLAB; + } + getDefaultGitUser(): string { return "Gitlab"; } diff --git a/test/service/configs/pullrequest/github-pr-configs-parser-multiple.test.ts b/test/service/configs/pullrequest/github-pr-configs-parser-multiple.test.ts index e1f7289..38adb10 100644 --- a/test/service/configs/pullrequest/github-pr-configs-parser-multiple.test.ts +++ b/test/service/configs/pullrequest/github-pr-configs-parser-multiple.test.ts @@ -4,7 +4,7 @@ import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs import GitClientFactory from "@bp/service/git/git-client-factory"; import { GitClientType } from "@bp/service/git/git.types"; import { mockGitHubClient } from "../../../support/mock/git-client-mock-support"; -import { resetProcessArgs } from "../../../support/utils"; +import { resetEnvTokens, resetProcessArgs } from "../../../support/utils"; import { MERGED_PR_FIXTURE, REPO, TARGET_OWNER, MULT_COMMITS_PR_FIXTURE } from "../../../support/mock/github-data"; import GitHubMapper from "@bp/service/git/github/github-mapper"; import GitHubClient from "@bp/service/git/github/github-client"; @@ -28,6 +28,9 @@ describe("github pull request config parser", () => { // reset process.env variables resetProcessArgs(); + // reset env tokens + resetEnvTokens(); + // mock octokit mockGitHubClient("http://localhost/api/v3"); diff --git a/test/service/configs/pullrequest/github-pr-configs-parser.test.ts b/test/service/configs/pullrequest/github-pr-configs-parser.test.ts index 974a02b..a45def3 100644 --- a/test/service/configs/pullrequest/github-pr-configs-parser.test.ts +++ b/test/service/configs/pullrequest/github-pr-configs-parser.test.ts @@ -1,10 +1,10 @@ import { Args } from "@bp/service/args/args.types"; -import { Configs } from "@bp/service/configs/configs.types"; +import { AuthTokenId, Configs } from "@bp/service/configs/configs.types"; import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser"; import GitClientFactory from "@bp/service/git/git-client-factory"; import { GitClientType } from "@bp/service/git/git.types"; import { mockGitHubClient } from "../../../support/mock/git-client-mock-support"; -import { addProcessArgs, createTestFile, removeTestFile, resetProcessArgs } from "../../../support/utils"; +import { addProcessArgs, createTestFile, removeTestFile, resetEnvTokens, resetProcessArgs } from "../../../support/utils"; import { MERGED_PR_FIXTURE, OPEN_PR_FIXTURE, NOT_MERGED_PR_FIXTURE, REPO, TARGET_OWNER, MULT_COMMITS_PR_FIXTURE } from "../../../support/mock/github-data"; import CLIArgsParser from "@bp/service/args/cli/cli-args-parser"; import GitHubMapper from "@bp/service/git/github/github-mapper"; @@ -66,6 +66,9 @@ describe("github pull request config parser", () => { // reset process.env variables resetProcessArgs(); + // reset env tokens + resetEnvTokens(); + // mock octokit mockGitHubClient("http://localhost/api/v3"); @@ -77,7 +80,6 @@ describe("github pull request config parser", () => { test("parse configs from pull request", async () => { const args: Args = { dryRun: false, - auth: "", pullRequest: mergedPRUrl, targetBranch: "prod", gitUser: "GitHub", @@ -99,7 +101,7 @@ describe("github pull request config parser", () => { user: "GitHub", email: "noreply@github.com" }); - expect(configs.auth).toEqual(""); + expect(configs.auth).toEqual(undefined); expect(configs.folder).toEqual(process.cwd() + "/bp"); expect(configs.originalPullRequest).toEqual({ number: 2368, @@ -840,4 +842,82 @@ describe("github pull request config parser", () => { comments: ["First comment", "Second comment"], }); }); + + test("override token using auth arg", async () => { + process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken"; + const args: Args = { + dryRun: true, + auth: "whatever", + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "GitHub", + gitEmail: "noreply@github.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual("whatever"); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "GitHub", + email: "noreply@github.com" + }); + }); + + test("auth using GITHUB_TOKEN has precedence over GIT_TOKEN env variable", async () => { + process.env[AuthTokenId.GIT_TOKEN] = "mygittoken"; + process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken"; + const args: Args = { + dryRun: true, + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "GitHub", + gitEmail: "noreply@github.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual("mygithubtoken"); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "GitHub", + email: "noreply@github.com" + }); + }); + + test("ignore env variables related to other git platforms", async () => { + process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken"; + process.env[AuthTokenId.CODEBERG_TOKEN] = "mycodebergtoken"; + const args: Args = { + dryRun: true, + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "GitHub", + gitEmail: "noreply@github.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual(undefined); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "GitHub", + email: "noreply@github.com" + }); + }); }); \ No newline at end of file diff --git a/test/service/configs/pullrequest/gitlab-pr-configs-parser-multiple.test.ts b/test/service/configs/pullrequest/gitlab-pr-configs-parser-multiple.test.ts index deff8de..842a9ca 100644 --- a/test/service/configs/pullrequest/gitlab-pr-configs-parser-multiple.test.ts +++ b/test/service/configs/pullrequest/gitlab-pr-configs-parser-multiple.test.ts @@ -7,6 +7,7 @@ import { getAxiosMocked } from "../../../support/mock/git-client-mock-support"; import { MERGED_SQUASHED_MR } from "../../../support/mock/gitlab-data"; import GitLabClient from "@bp/service/git/gitlab/gitlab-client"; import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper"; +import { resetEnvTokens } from "../../../support/utils"; jest.spyOn(GitLabMapper.prototype, "mapPullRequest"); jest.spyOn(GitLabClient.prototype, "getPullRequest"); @@ -31,6 +32,9 @@ describe("gitlab merge request config parser", () => { }); beforeEach(() => { + // reset env tokens + resetEnvTokens(); + configParser = new PullRequestConfigsParser(); }); diff --git a/test/service/configs/pullrequest/gitlab-pr-configs-parser.test.ts b/test/service/configs/pullrequest/gitlab-pr-configs-parser.test.ts index 75057ca..0c9248d 100644 --- a/test/service/configs/pullrequest/gitlab-pr-configs-parser.test.ts +++ b/test/service/configs/pullrequest/gitlab-pr-configs-parser.test.ts @@ -1,12 +1,12 @@ import { Args } from "@bp/service/args/args.types"; -import { Configs } from "@bp/service/configs/configs.types"; +import { AuthTokenId, Configs } from "@bp/service/configs/configs.types"; import PullRequestConfigsParser from "@bp/service/configs/pullrequest/pr-configs-parser"; import GitClientFactory from "@bp/service/git/git-client-factory"; import { GitClientType } from "@bp/service/git/git.types"; import { getAxiosMocked } from "../../../support/mock/git-client-mock-support"; import { CLOSED_NOT_MERGED_MR, MERGED_SQUASHED_MR, OPEN_MR } from "../../../support/mock/gitlab-data"; import GHAArgsParser from "@bp/service/args/gha/gha-args-parser"; -import { createTestFile, removeTestFile, spyGetInput } from "../../../support/utils"; +import { createTestFile, removeTestFile, resetEnvTokens, spyGetInput } from "../../../support/utils"; import GitLabClient from "@bp/service/git/gitlab/gitlab-client"; import GitLabMapper from "@bp/service/git/gitlab/gitlab-mapper"; @@ -70,6 +70,9 @@ describe("gitlab merge request config parser", () => { }); beforeEach(() => { + // reset env tokens + resetEnvTokens(); + argsParser = new GHAArgsParser(); configParser = new PullRequestConfigsParser(); }); @@ -795,4 +798,97 @@ describe("gitlab merge request config parser", () => { comments: ["First comment", "Second comment"], }); }); + + test("override token using auth arg", async () => { + process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken"; + const args: Args = { + dryRun: true, + auth: "whatever", + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "Gitlab", + gitEmail: "noreply@gitlab.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1); + expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual("whatever"); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "Gitlab", + email: "noreply@gitlab.com" + }); + }); + + test("auth using GITLAB_TOKEN has precedence over GIT_TOKEN env variable", async () => { + process.env[AuthTokenId.GIT_TOKEN] = "mygittoken"; + process.env[AuthTokenId.GITLAB_TOKEN] = "mygitlabtoken"; + const args: Args = { + dryRun: true, + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "Gitlab", + gitEmail: "noreply@gitlab.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1); + expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual("mygitlabtoken"); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "Gitlab", + email: "noreply@gitlab.com" + }); + }); + + test("ignore env variables related to other git platforms", async () => { + process.env[AuthTokenId.CODEBERG_TOKEN] = "mycodebergtoken"; + process.env[AuthTokenId.GITHUB_TOKEN] = "mygithubtoken"; + const args: Args = { + dryRun: true, + pullRequest: mergedPRUrl, + targetBranch: "prod", + folder: "/tmp/test", + gitUser: "Gitlab", + gitEmail: "noreply@gitlab.com", + reviewers: [], + assignees: [], + inheritReviewers: true, + }; + + const configs: Configs = await configParser.parseAndValidate(args); + + expect(GitLabClient.prototype.getPullRequest).toBeCalledTimes(1); + expect(GitLabClient.prototype.getPullRequest).toBeCalledWith("superuser", "backporting-example", 1, true); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledTimes(1); + expect(GitLabMapper.prototype.mapPullRequest).toBeCalledWith(expect.anything(), []); + + expect(configs.dryRun).toEqual(true); + expect(configs.auth).toEqual(undefined); + expect(configs.folder).toEqual("/tmp/test"); + expect(configs.git).toEqual({ + user: "Gitlab", + email: "noreply@gitlab.com" + }); + }); }); \ No newline at end of file diff --git a/test/support/utils.ts b/test/support/utils.ts index 693610d..e470906 100644 --- a/test/support/utils.ts +++ b/test/support/utils.ts @@ -1,4 +1,5 @@ import * as core from "@actions/core"; +import { AuthTokenId } from "@bp/service/configs/configs.types"; import * as fs from "fs"; export const addProcessArgs = (args: string[]) => { @@ -9,6 +10,13 @@ export const resetProcessArgs = () => { process.argv = ["node", "backporting"]; }; +export const resetEnvTokens = () => { + delete process.env[AuthTokenId.GITHUB_TOKEN]; + delete process.env[AuthTokenId.GITLAB_TOKEN]; + delete process.env[AuthTokenId.CODEBERG_TOKEN]; + delete process.env[AuthTokenId.GIT_TOKEN]; +}; + // eslint-disable-next-line @typescript-eslint/no-explicit-any export const spyGetInput = (obj: any) => { const mock = jest.spyOn(core, "getInput");