summaryrefslogtreecommitdiff
path: root/gitlab
diff options
context:
space:
mode:
Diffstat (limited to 'gitlab')
-rw-r--r--gitlab/cli.py108
-rw-r--r--gitlab/client.py82
-rw-r--r--gitlab/config.py4
3 files changed, 187 insertions, 7 deletions
diff --git a/gitlab/cli.py b/gitlab/cli.py
index c1a1334..a48b53b 100644
--- a/gitlab/cli.py
+++ b/gitlab/cli.py
@@ -19,6 +19,7 @@
import argparse
import functools
+import os
import re
import sys
from types import ModuleType
@@ -112,17 +113,25 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
"-v",
"--verbose",
"--fancy",
- help="Verbose mode (legacy format only)",
+ help="Verbose mode (legacy format only) [env var: GITLAB_VERBOSE]",
action="store_true",
+ default=os.getenv("GITLAB_VERBOSE"),
)
parser.add_argument(
- "-d", "--debug", help="Debug mode (display HTTP requests)", action="store_true"
+ "-d",
+ "--debug",
+ help="Debug mode (display HTTP requests) [env var: GITLAB_DEBUG]",
+ action="store_true",
+ default=os.getenv("GITLAB_DEBUG"),
)
parser.add_argument(
"-c",
"--config-file",
action="append",
- help="Configuration file to use. Can be used multiple times.",
+ help=(
+ "Configuration file to use. Can be used multiple times. "
+ "[env var: PYTHON_GITLAB_CFG]"
+ ),
)
parser.add_argument(
"-g",
@@ -151,7 +160,86 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
),
required=False,
)
+ parser.add_argument(
+ "--server-url",
+ help=("GitLab server URL [env var: GITLAB_URL]"),
+ required=False,
+ default=os.getenv("GITLAB_URL"),
+ )
+ parser.add_argument(
+ "--ssl-verify",
+ help=(
+ "Whether SSL certificates should be validated. [env var: GITLAB_SSL_VERIFY]"
+ ),
+ required=False,
+ default=os.getenv("GITLAB_SSL_VERIFY"),
+ )
+ parser.add_argument(
+ "--timeout",
+ help=(
+ "Timeout to use for requests to the GitLab server. "
+ "[env var: GITLAB_TIMEOUT]"
+ ),
+ required=False,
+ default=os.getenv("GITLAB_TIMEOUT"),
+ )
+ parser.add_argument(
+ "--api-version",
+ help=("GitLab API version [env var: GITLAB_API_VERSION]"),
+ required=False,
+ default=os.getenv("GITLAB_API_VERSION"),
+ )
+ parser.add_argument(
+ "--per-page",
+ help=(
+ "Number of entries to return per page in the response. "
+ "[env var: GITLAB_PER_PAGE]"
+ ),
+ required=False,
+ default=os.getenv("GITLAB_PER_PAGE"),
+ )
+ parser.add_argument(
+ "--pagination",
+ help=(
+ "Whether to use keyset or offset pagination [env var: GITLAB_PAGINATION]"
+ ),
+ required=False,
+ default=os.getenv("GITLAB_PAGINATION"),
+ )
+ parser.add_argument(
+ "--order-by",
+ help=("Set order_by globally [env var: GITLAB_ORDER_BY]"),
+ required=False,
+ default=os.getenv("GITLAB_ORDER_BY"),
+ )
+ parser.add_argument(
+ "--user-agent",
+ help=(
+ "The user agent to send to GitLab with the HTTP request. "
+ "[env var: GITLAB_USER_AGENT]"
+ ),
+ required=False,
+ default=os.getenv("GITLAB_USER_AGENT"),
+ )
+ tokens = parser.add_mutually_exclusive_group()
+ tokens.add_argument(
+ "--private-token",
+ help=("GitLab private access token [env var: GITLAB_PRIVATE_TOKEN]"),
+ required=False,
+ default=os.getenv("GITLAB_PRIVATE_TOKEN"),
+ )
+ tokens.add_argument(
+ "--oauth-token",
+ help=("GitLab OAuth token [env var: GITLAB_OAUTH_TOKEN]"),
+ required=False,
+ default=os.getenv("GITLAB_OAUTH_TOKEN"),
+ )
+ tokens.add_argument(
+ "--job-token",
+ help=("GitLab CI job token [env var: CI_JOB_TOKEN]"),
+ required=False,
+ )
return parser
@@ -243,13 +331,23 @@ def main() -> None:
"whaction",
"version",
"output",
+ "fields",
+ "server_url",
+ "ssl_verify",
+ "timeout",
+ "api_version",
+ "pagination",
+ "user_agent",
+ "private_token",
+ "oauth_token",
+ "job_token",
):
args_dict.pop(item)
args_dict = {k: _parse_value(v) for k, v in args_dict.items() if v is not None}
try:
- gl = gitlab.Gitlab.from_config(gitlab_id, config_files)
- if gl.private_token or gl.oauth_token or gl.job_token:
+ gl = gitlab.Gitlab.merge_config(vars(options), gitlab_id, config_files)
+ if gl.private_token or gl.oauth_token:
gl.auth()
except Exception as e:
die(str(e))
diff --git a/gitlab/client.py b/gitlab/client.py
index c1e0825..b791c8f 100644
--- a/gitlab/client.py
+++ b/gitlab/client.py
@@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""Wrapper for the GitLab API."""
+import os
import time
from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
@@ -256,6 +257,87 @@ class Gitlab(object):
retry_transient_errors=config.retry_transient_errors,
)
+ @classmethod
+ def merge_config(
+ cls,
+ options: dict,
+ gitlab_id: Optional[str] = None,
+ config_files: Optional[List[str]] = None,
+ ) -> "Gitlab":
+ """Create a Gitlab connection by merging configuration with
+ the following precedence:
+
+ 1. Explicitly provided CLI arguments,
+ 2. Environment variables,
+ 3. Configuration files:
+ a. explicitly defined config files:
+ i. via the `--config-file` CLI argument,
+ ii. via the `PYTHON_GITLAB_CFG` environment variable,
+ b. user-specific config file,
+ c. system-level config file,
+ 4. Environment variables always present in CI (CI_SERVER_URL, CI_JOB_TOKEN).
+
+ Args:
+ options: A dictionary of explicitly provided key-value options.
+ gitlab_id: ID of the configuration section.
+ config_files: List of paths to configuration files.
+ Returns:
+ (gitlab.Gitlab): A Gitlab connection.
+
+ Raises:
+ gitlab.config.GitlabDataError: If the configuration is not correct.
+ """
+ config = gitlab.config.GitlabConfigParser(
+ gitlab_id=gitlab_id, config_files=config_files
+ )
+ url = (
+ options.get("server_url")
+ or config.url
+ or os.getenv("CI_SERVER_URL")
+ or gitlab.const.DEFAULT_URL
+ )
+ private_token, oauth_token, job_token = cls._merge_auth(options, config)
+
+ return cls(
+ url=url,
+ private_token=private_token,
+ oauth_token=oauth_token,
+ job_token=job_token,
+ ssl_verify=options.get("ssl_verify") or config.ssl_verify,
+ timeout=options.get("timeout") or config.timeout,
+ api_version=options.get("api_version") or config.api_version,
+ per_page=options.get("per_page") or config.per_page,
+ pagination=options.get("pagination") or config.pagination,
+ order_by=options.get("order_by") or config.order_by,
+ user_agent=options.get("user_agent") or config.user_agent,
+ )
+
+ @staticmethod
+ def _merge_auth(options: dict, config: gitlab.config.GitlabConfigParser) -> Tuple:
+ """
+ Return a tuple where at most one of 3 token types ever has a value.
+ Since multiple types of tokens may be present in the environment,
+ options, or config files, this precedence ensures we don't
+ inadvertently cause errors when initializing the client.
+
+ This is especially relevant when executed in CI where user and
+ CI-provided values are both available.
+ """
+ private_token = options.get("private_token") or config.private_token
+ oauth_token = options.get("oauth_token") or config.oauth_token
+ job_token = (
+ options.get("job_token") or config.job_token or os.getenv("CI_JOB_TOKEN")
+ )
+
+ if private_token:
+ return (private_token, None, None)
+ if oauth_token:
+ return (None, oauth_token, None)
+ if job_token:
+ return (None, None, job_token)
+
+ return (None, None, None)
+
def auth(self) -> None:
"""Performs an authentication using private token.
diff --git a/gitlab/config.py b/gitlab/config.py
index 154f063..c11a4e9 100644
--- a/gitlab/config.py
+++ b/gitlab/config.py
@@ -23,7 +23,7 @@ from os.path import expanduser, expandvars
from pathlib import Path
from typing import List, Optional, Union
-from gitlab.const import DEFAULT_URL, USER_AGENT
+from gitlab.const import USER_AGENT
_DEFAULT_FILES: List[str] = [
"/etc/python-gitlab.cfg",
@@ -119,7 +119,7 @@ class GitlabConfigParser(object):
self.retry_transient_errors: bool = False
self.ssl_verify: Union[bool, str] = True
self.timeout: int = 60
- self.url: str = DEFAULT_URL
+ self.url: Optional[str] = None
self.user_agent: str = USER_AGENT
self._files = _get_config_files(config_files)