Skip to content

Commit 13467c5

Browse files
committed
feat(cli): allow options from args and environment variables
1 parent ce4bc0d commit 13467c5

File tree

2 files changed

+141
-5
lines changed

2 files changed

+141
-5
lines changed

gitlab/cli.py

+90-5
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import argparse
2121
import functools
22+
import os
2223
import re
2324
import sys
2425
from types import ModuleType
@@ -112,17 +113,22 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
112113
"-v",
113114
"--verbose",
114115
"--fancy",
115-
help="Verbose mode (legacy format only)",
116+
help="Verbose mode (legacy format only) [env var: GITLAB_VERBOSE]",
116117
action="store_true",
118+
default=os.getenv("GITLAB_VERBOSE"),
117119
)
118120
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"),
120126
)
121127
parser.add_argument(
122128
"-c",
123129
"--config-file",
124130
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]",
126132
)
127133
parser.add_argument(
128134
"-g",
@@ -151,7 +157,86 @@ def _get_base_parser(add_help: bool = True) -> argparse.ArgumentParser:
151157
),
152158
required=False,
153159
)
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+
)
155240
return parser
156241

157242

@@ -248,7 +333,7 @@ def main() -> None:
248333
args_dict = {k: _parse_value(v) for k, v in args_dict.items() if v is not None}
249334

250335
try:
251-
gl = gitlab.Gitlab.from_config(gitlab_id, config_files)
336+
gl = gitlab.Gitlab.merge_config(options, gitlab_id, config_files)
252337
if gl.private_token or gl.oauth_token or gl.job_token:
253338
gl.auth()
254339
except Exception as e:

gitlab/client.py

+51
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
"""Wrapper for the GitLab API."""
1818

19+
import os
1920
import time
21+
from argparse import Namespace
2022
from typing import Any, cast, Dict, List, Optional, Tuple, TYPE_CHECKING, Union
2123

2224
import requests
@@ -254,6 +256,55 @@ def from_config(
254256
retry_transient_errors=config.retry_transient_errors,
255257
)
256258

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+
257308
def auth(self) -> None:
258309
"""Performs an authentication using private token.
259310

0 commit comments

Comments
 (0)