GeezerVison is showing github.com — feat(cli): allow options from args and environment variables · python-gitlab/python-gitlab@a998970
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: gitlab/cli.py +90-5Lines changed: 90 additions & 5 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 19 | 19 |
| 20 | 20 | import argparse | 21 | 21 | import functools | | 22 | +import os | 22 | 23 | import re | 23 | 24 | import sys | 24 | 25 | from types import ModuleType | @@ -112,17 +113,22 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser: | 112 | 113 | "-v", | 113 | 114 | "--verbose", | 114 | 115 | "--fancy", | 115 | | - help="Verbose mode (legacy format only)", | | 116 | + help="Verbose mode (legacy format only) [env var: GITLAB_VERBOSE]", | 116 | 117 | action="store_true", | | 118 | + default=os.getenv("GITLAB_VERBOSE"), | 117 | 119 | ) | 118 | 120 | parser.add_argument( | 119 | | - "-d", "--debug", help="Debug mode (display HTTP requests)", action="store_true" | | 121 | + "-d", | | 122 | + "--debug", | | 123 | + help="Debug mode (display HTTP requests) [env var: GITLAB_DEBUG]", | | 124 | + action="store_true", | | 125 | + default=os.getenv("GITLAB_DEBUG"), | 120 | 126 | ) | 121 | 127 | parser.add_argument( | 122 | 128 | "-c", | 123 | 129 | "--config-file", | 124 | 130 | action="append", | 125 | | - help="Configuration file to use. Can be used multiple times.", | | 131 | + help="Configuration file to use. Can be used multiple times. [env var: PYTHON_GITLAB_CFG]", | 126 | 132 | ) | 127 | 133 | parser.add_argument( | 128 | 134 | "-g", | @@ -151,7 +157,86 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser: | 151 | 157 | ), | 152 | 158 | required=False, | 153 | 159 | ) | 154 | | - | | 160 | + # TODO: deduplicate everything by providing some data struct to loop over | | 161 | + parser.add_argument( | | 162 | + "--url", | | 163 | + help=("GitLab server URL [env var: GITLAB_URL]"), | | 164 | + required=False, | | 165 | + default=os.getenv("GITLAB_URL"), | | 166 | + ) | | 167 | + parser.add_argument( | | 168 | + "--private-token", | | 169 | + help=("GitLab private token [env var: GITLAB_PRIVATE_TOKEN]"), | | 170 | + required=False, | | 171 | + default=os.getenv("GITLAB_PRIVATE_TOKEN"), | | 172 | + ) | | 173 | + parser.add_argument( | | 174 | + "--oauth-token", | | 175 | + help=("GitLab OAuth token [env var: GITLAB_OAUTH_TOKEN]"), | | 176 | + required=False, | | 177 | + default=os.getenv("GITLAB_OAUTH_TOKEN"), | | 178 | + ) | | 179 | + parser.add_argument( | | 180 | + "--job-token", | | 181 | + help=( | | 182 | + "GitLab CI job token. Explicitly providing this is usually not needed.\n" | | 183 | + "[env var, only if explicitly overriding CI_JOB_TOKEN: GITLAB_JOB_TOKEN]" | | 184 | + ), | | 185 | + required=False, | | 186 | + default=os.getenv("GITLAB_JOB_TOKEN"), | | 187 | + ) | | 188 | + parser.add_argument( | | 189 | + "--ssl-verify", | | 190 | + help=( | | 191 | + "Whether SSL certificates should be validated. [env var: GITLAB_SSL_VERIFY]" | | 192 | + ), | | 193 | + required=False, | | 194 | + default=os.getenv("GITLAB_SSL_VERIFY"), | | 195 | + ) | | 196 | + parser.add_argument( | | 197 | + "--timeout", | | 198 | + help=( | | 199 | + "Timeout to use for requests to the GitLab server. [env var: GITLAB_TIMEOUT]" | | 200 | + ), | | 201 | + required=False, | | 202 | + default=os.getenv("GITLAB_TIMEOUT"), | | 203 | + ) | | 204 | + parser.add_argument( | | 205 | + "--api-version", | | 206 | + help=("GitLab API version [env var: GITLAB_API_VERSION]"), | | 207 | + required=False, | | 208 | + default=os.getenv("GITLAB_API_VERSION"), | | 209 | + ) | | 210 | + parser.add_argument( | | 211 | + "--per-page", | | 212 | + help=( | | 213 | + "Number of entries to return per page in the response. [env var: GITLAB_PER_PAGE]" | | 214 | + ), | | 215 | + required=False, | | 216 | + default=os.getenv("GITLAB_PER_PAGE"), | | 217 | + ) | | 218 | + parser.add_argument( | | 219 | + "--pagination", | | 220 | + help=( | | 221 | + "Whether to use keyset or offset pagination [env var: GITLAB_PAGINATION]" | | 222 | + ), | | 223 | + required=False, | | 224 | + default=os.getenv("GITLAB_PAGINATION"), | | 225 | + ) | | 226 | + parser.add_argument( | | 227 | + "--order-by", | | 228 | + help=("Set order_by globally [env var: GITLAB_ORDER_BY]"), | | 229 | + required=False, | | 230 | + default=os.getenv("GITLAB_ORDER_BY"), | | 231 | + ) | | 232 | + parser.add_argument( | | 233 | + "--user-agent", | | 234 | + help=( | | 235 | + "The user agent to send to GitLab with the HTTP request. [env var: GITLAB_USER_AGENT]" | | 236 | + ), | | 237 | + required=False, | | 238 | + default=os.getenv("GITLAB_USER_AGENT"), | | 239 | + ) | 155 | 240 | return parser | 156 | 241 |
| 157 | 242 |
| @@ -248,7 +333,7 @@ def main() -> None: | 248 | 333 | args_dict = {k: _parse_value(v) for k, v in args_dict.items() if v is not None} | 249 | 334 |
| 250 | 335 | try: | 251 | | - gl = gitlab.Gitlab.from_config(gitlab_id, config_files) | | 336 | + gl = gitlab.Gitlab.merge_config(options, gitlab_id, config_files) | 252 | 337 | if gl.private_token or gl.oauth_token or gl.job_token: | 253 | 338 | gl.auth() | 254 | 339 | except Exception as e: | |
Collapse file Copy file name to clipboardExpand all lines: gitlab/client.py +51Lines changed: 51 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 16 | 16 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | 17 | 17 | """Wrapper for the GitLab API.""" | 18 | 18 |
| | 19 | +import os | 19 | 20 | import time | | 21 | +from argparse import Namespace | 20 | 22 | from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union | 21 | 23 |
| 22 | 24 | import requests | @@ -254,6 +256,55 @@ def from_config( | 254 | 256 | retry_transient_errors=config.retry_transient_errors, | 255 | 257 | ) | 256 | 258 |
| | 259 | + @classmethod | | 260 | + def merge_config( | | 261 | + cls, | | 262 | + options: Namespace, | | 263 | + gitlab_id: Optional[str] = None, | | 264 | + config_files: Optional[List[str]] = None, | | 265 | + ) -> "Gitlab": | | 266 | + """Create a Gitlab connection by merging configuration with | | 267 | + the following precedence: | | 268 | +
| | 269 | + 1. Explicitly provided CLI arguments, | | 270 | + 2. Environment variables, | | 271 | + 3. Configuration files: | | 272 | + a. explicitly defined config files: | | 273 | + i. via the `--config-file` CLI argument, | | 274 | + ii. via the `PYTHON_GITLAB_CFG` environment variable, | | 275 | + b. user-specific config file, | | 276 | + c. system-level config file, | | 277 | + 4. Environment variables always present in CI (CI_SERVER_URL, CI_JOB_TOKEN). | | 278 | +
| | 279 | + Args: | | 280 | + options list[str]: List of options provided via the CLI. | | 281 | + gitlab_id (str): ID of the configuration section. | | 282 | + config_files list[str]: List of paths to configuration files. | | 283 | + Returns: | | 284 | + (gitlab.Gitlab): A Gitlab connection. | | 285 | +
| | 286 | + Raises: | | 287 | + gitlab.config.GitlabDataError: If the configuration is not correct. | | 288 | + """ | | 289 | + config = gitlab.config.GitlabConfigParser( | | 290 | + gitlab_id=gitlab_id, config_files=config_files | | 291 | + ) | | 292 | + return cls( | | 293 | + url=options.url or config.url or os.getenv("CI_SERVER_URL"), | | 294 | + private_token=options.private_token or config.private_token, | | 295 | + oauth_token=options.oauth_token or config.oauth_token, | | 296 | + job_token=options.job_token | | 297 | + or config.job_token | | 298 | + or os.getenv("CI_JOB_TOKEN"), | | 299 | + ssl_verify=options.ssl_verify or config.ssl_verify, | | 300 | + timeout=options.timeout or config.timeout, | | 301 | + api_version=options.api_version or config.api_version, | | 302 | + per_page=options.per_page or config.per_page, | | 303 | + pagination=options.pagination or config.pagination, | | 304 | + order_by=options.order_by or config.order_by, | | 305 | + user_agent=options.user_agent or config.user_agent, | | 306 | + ) | | 307 | + | 257 | 308 | def auth(self) -> None: | 258 | 309 | """Performs an authentication using private token. | 259 | 310 |
| |
|