Skip to content

Commit d0c6005

Browse files
committed
feat(cli): do not require config file to run CLI
BREAKING CHANGE: A config file is no longer needed to run the CLI. python-gitlab will default to https://gitlab.com with no authentication if there is no config file provided. python-gitlab will now also only look for configuration in the provided PYTHON_GITLAB_CFG path, instead of merging it with user- and system-wide config files. If the environment variable is defined and the file cannot be opened, python-gitlab will now explicitly fail.
1 parent af33aff commit d0c6005

File tree

3 files changed

+180
-115
lines changed

3 files changed

+180
-115
lines changed

docs/cli-usage.rst

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44

55
``python-gitlab`` provides a :command:`gitlab` command-line tool to interact
66
with GitLab servers. It uses a configuration file to define how to connect to
7-
the servers.
7+
the servers. Without a configuration file, ``gitlab`` will default to
8+
https://gitlab.com and unauthenticated requests.
89

910
.. _cli_configuration:
1011

@@ -16,8 +17,8 @@ Files
1617

1718
``gitlab`` looks up 3 configuration files by default:
1819

19-
``PYTHON_GITLAB_CFG`` environment variable
20-
An environment variable that contains the path to a configuration file
20+
The ``PYTHON_GITLAB_CFG`` environment variable
21+
An environment variable that contains the path to a configuration file.
2122

2223
``/etc/python-gitlab.cfg``
2324
System-wide configuration file
@@ -27,6 +28,13 @@ Files
2728

2829
You can use a different configuration file with the ``--config-file`` option.
2930

31+
.. warning::
32+
If the ``PYTHON_GITLAB_CFG`` environment variable is defined and the target
33+
file exists, it will be the only configuration file parsed by ``gitlab``.
34+
35+
If the environment variable is defined and the target file cannot be accessed,
36+
``gitlab`` will fail explicitly.
37+
3038
Content
3139
-------
3240

gitlab/config.py

Lines changed: 81 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,54 @@
2020
import shlex
2121
import subprocess
2222
from os.path import expanduser, expandvars
23+
from pathlib import Path
2324
from typing import List, Optional, Union
2425

25-
from gitlab.const import USER_AGENT
26+
from gitlab.const import DEFAULT_URL, USER_AGENT
2627

27-
28-
def _env_config() -> List[str]:
29-
if "PYTHON_GITLAB_CFG" in os.environ:
30-
return [os.environ["PYTHON_GITLAB_CFG"]]
31-
return []
32-
33-
34-
_DEFAULT_FILES: List[str] = _env_config() + [
28+
_DEFAULT_FILES: List[str] = [
3529
"/etc/python-gitlab.cfg",
36-
os.path.expanduser("~/.python-gitlab.cfg"),
30+
str(Path.home() / ".python-gitlab.cfg"),
3731
]
3832

3933
HELPER_PREFIX = "helper:"
4034

4135
HELPER_ATTRIBUTES = ["job_token", "http_password", "private_token", "oauth_token"]
4236

4337

38+
def _resolve_file(filepath: Union[Path, str]) -> str:
39+
resolved = Path(filepath).resolve(strict=True)
40+
return str(resolved)
41+
42+
43+
def _get_config_files(
44+
config_files: Optional[List[str]] = None,
45+
) -> Union[str, List[str]]:
46+
if config_files:
47+
return config_files
48+
49+
try:
50+
env_config = os.environ["PYTHON_GITLAB_CFG"]
51+
return _resolve_file(env_config)
52+
except KeyError:
53+
pass
54+
except OSError as e:
55+
raise GitlabConfigMissingError(
56+
f"Cannot read config from PYTHON_GITLAB_CFG in " f"{env_config}: {e}"
57+
)
58+
59+
default_files = []
60+
61+
for config_file in _DEFAULT_FILES:
62+
try:
63+
resolved = _resolve_file(config_file)
64+
except OSError:
65+
continue
66+
default_files.append(resolved)
67+
68+
return default_files
69+
70+
4471
class ConfigError(Exception):
4572
pass
4673

@@ -66,155 +93,149 @@ def __init__(
6693
self, gitlab_id: Optional[str] = None, config_files: Optional[List[str]] = None
6794
) -> None:
6895
self.gitlab_id = gitlab_id
69-
_files = config_files or _DEFAULT_FILES
70-
file_exist = False
71-
for file in _files:
72-
if os.path.exists(file):
73-
file_exist = True
74-
if not file_exist:
75-
raise GitlabConfigMissingError(
76-
"Config file not found. \nPlease create one in "
77-
"one of the following locations: {} \nor "
78-
"specify a config file using the '-c' parameter.".format(
79-
", ".join(_DEFAULT_FILES)
80-
)
81-
)
96+
self.http_username: Optional[str] = None
97+
self.http_password: Optional[str] = None
98+
self.job_token: Optional[str] = None
99+
self.oauth_token: Optional[str] = None
100+
self.private_token: Optional[str] = None
101+
102+
self.api_version: str = "4"
103+
self.order_by: Optional[str] = None
104+
self.pagination: Optional[str] = None
105+
self.per_page: Optional[int] = None
106+
self.retry_transient_errors: bool = False
107+
self.ssl_verify: Union[bool, str] = True
108+
self.timeout: int = 60
109+
self.url: str = DEFAULT_URL
110+
self.user_agent: str = USER_AGENT
82111

83-
self._config = configparser.ConfigParser()
84-
self._config.read(_files)
112+
self._files = _get_config_files(config_files)
113+
if self._files:
114+
self._parse_config()
115+
116+
def _parse_config(self) -> None:
117+
_config = configparser.ConfigParser()
118+
_config.read(self._files)
85119

86120
if self.gitlab_id is None:
87121
try:
88-
self.gitlab_id = self._config.get("global", "default")
122+
self.gitlab_id = _config.get("global", "default")
89123
except Exception as e:
90124
raise GitlabIDError(
91125
"Impossible to get the gitlab id (not specified in config file)"
92126
) from e
93127

94128
try:
95-
self.url = self._config.get(self.gitlab_id, "url")
129+
self.url = _config.get(self.gitlab_id, "url")
96130
except Exception as e:
97131
raise GitlabDataError(
98132
"Impossible to get gitlab details from "
99133
f"configuration ({self.gitlab_id})"
100134
) from e
101135

102-
self.ssl_verify: Union[bool, str] = True
103136
try:
104-
self.ssl_verify = self._config.getboolean("global", "ssl_verify")
137+
self.ssl_verify = _config.getboolean("global", "ssl_verify")
105138
except ValueError:
106139
# Value Error means the option exists but isn't a boolean.
107140
# Get as a string instead as it should then be a local path to a
108141
# CA bundle.
109142
try:
110-
self.ssl_verify = self._config.get("global", "ssl_verify")
143+
self.ssl_verify = _config.get("global", "ssl_verify")
111144
except Exception:
112145
pass
113146
except Exception:
114147
pass
115148
try:
116-
self.ssl_verify = self._config.getboolean(self.gitlab_id, "ssl_verify")
149+
self.ssl_verify = _config.getboolean(self.gitlab_id, "ssl_verify")
117150
except ValueError:
118151
# Value Error means the option exists but isn't a boolean.
119152
# Get as a string instead as it should then be a local path to a
120153
# CA bundle.
121154
try:
122-
self.ssl_verify = self._config.get(self.gitlab_id, "ssl_verify")
155+
self.ssl_verify = _config.get(self.gitlab_id, "ssl_verify")
123156
except Exception:
124157
pass
125158
except Exception:
126159
pass
127160

128-
self.timeout = 60
129161
try:
130-
self.timeout = self._config.getint("global", "timeout")
162+
self.timeout = _config.getint("global", "timeout")
131163
except Exception:
132164
pass
133165
try:
134-
self.timeout = self._config.getint(self.gitlab_id, "timeout")
166+
self.timeout = _config.getint(self.gitlab_id, "timeout")
135167
except Exception:
136168
pass
137169

138-
self.private_token = None
139170
try:
140-
self.private_token = self._config.get(self.gitlab_id, "private_token")
171+
self.private_token = _config.get(self.gitlab_id, "private_token")
141172
except Exception:
142173
pass
143174

144-
self.oauth_token = None
145175
try:
146-
self.oauth_token = self._config.get(self.gitlab_id, "oauth_token")
176+
self.oauth_token = _config.get(self.gitlab_id, "oauth_token")
147177
except Exception:
148178
pass
149179

150-
self.job_token = None
151180
try:
152-
self.job_token = self._config.get(self.gitlab_id, "job_token")
181+
self.job_token = _config.get(self.gitlab_id, "job_token")
153182
except Exception:
154183
pass
155184

156-
self.http_username = None
157-
self.http_password = None
158185
try:
159-
self.http_username = self._config.get(self.gitlab_id, "http_username")
160-
self.http_password = self._config.get(self.gitlab_id, "http_password")
186+
self.http_username = _config.get(self.gitlab_id, "http_username")
187+
self.http_password = _config.get(self.gitlab_id, "http_password")
161188
except Exception:
162189
pass
163190

164191
self._get_values_from_helper()
165192

166-
self.api_version = "4"
167193
try:
168-
self.api_version = self._config.get("global", "api_version")
194+
self.api_version = _config.get("global", "api_version")
169195
except Exception:
170196
pass
171197
try:
172-
self.api_version = self._config.get(self.gitlab_id, "api_version")
198+
self.api_version = _config.get(self.gitlab_id, "api_version")
173199
except Exception:
174200
pass
175201
if self.api_version not in ("4",):
176202
raise GitlabDataError(f"Unsupported API version: {self.api_version}")
177203

178-
self.per_page = None
179204
for section in ["global", self.gitlab_id]:
180205
try:
181-
self.per_page = self._config.getint(section, "per_page")
206+
self.per_page = _config.getint(section, "per_page")
182207
except Exception:
183208
pass
184209
if self.per_page is not None and not 0 <= self.per_page <= 100:
185210
raise GitlabDataError(f"Unsupported per_page number: {self.per_page}")
186211

187-
self.pagination = None
188212
try:
189-
self.pagination = self._config.get(self.gitlab_id, "pagination")
213+
self.pagination = _config.get(self.gitlab_id, "pagination")
190214
except Exception:
191215
pass
192216

193-
self.order_by = None
194217
try:
195-
self.order_by = self._config.get(self.gitlab_id, "order_by")
218+
self.order_by = _config.get(self.gitlab_id, "order_by")
196219
except Exception:
197220
pass
198221

199-
self.user_agent = USER_AGENT
200222
try:
201-
self.user_agent = self._config.get("global", "user_agent")
223+
self.user_agent = _config.get("global", "user_agent")
202224
except Exception:
203225
pass
204226
try:
205-
self.user_agent = self._config.get(self.gitlab_id, "user_agent")
227+
self.user_agent = _config.get(self.gitlab_id, "user_agent")
206228
except Exception:
207229
pass
208230

209-
self.retry_transient_errors = False
210231
try:
211-
self.retry_transient_errors = self._config.getboolean(
232+
self.retry_transient_errors = _config.getboolean(
212233
"global", "retry_transient_errors"
213234
)
214235
except Exception:
215236
pass
216237
try:
217-
self.retry_transient_errors = self._config.getboolean(
238+
self.retry_transient_errors = _config.getboolean(
218239
self.gitlab_id, "retry_transient_errors"
219240
)
220241
except Exception:

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy