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

+11-3
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

+81-60
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)