git-glimpse-cli 0.1.1__tar.gz → 0.1.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-glimpse-cli
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: CLI tool to manage and update shared GitHub repos with GitGlimpse.
5
5
  Author-email: sutaC <contact@sutac.pl>
6
6
  License-Expression: MIT
@@ -65,12 +65,18 @@ pip install git-glimpse-cli
65
65
 
66
66
  GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.json`, and local repository settings are stored at `./.shared-repo.json`.
67
67
 
68
+ Configuration hierarchy:
69
+
70
+ ```
71
+ Most important -> Local repo config -> Global cli config
72
+ ```
73
+
68
74
  ### Advanced Settings
69
75
 
70
- | Command | Description |
71
- | ------------------------ | --------------------------------------------------------------------------- |
72
- | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
73
- | `glimpse config set-url` | Overrides the default production server with a custom backend or localhost. |
76
+ | Command | Description |
77
+ | ---------------------- | --------------------------------------------------------------------------- |
78
+ | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
79
+ | `glimpse config url` | Overrides the default production server with a custom backend or localhost. |
74
80
 
75
81
  ## Development
76
82
 
@@ -82,7 +88,7 @@ GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.jso
82
88
  source .venv/bin/activate
83
89
  pip install -e .
84
90
  ```
85
- 2. Point the CLI to your local development server:
91
+ 2. Point the CLI to your local development server (use `--local` to not propagate over your real global config):
86
92
  ```bash
87
- glimpse config set-url http://localhost:5000
93
+ glimpse config url http://localhost:5000 --local
88
94
  ```
@@ -48,12 +48,18 @@ pip install git-glimpse-cli
48
48
 
49
49
  GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.json`, and local repository settings are stored at `./.shared-repo.json`.
50
50
 
51
+ Configuration hierarchy:
52
+
53
+ ```
54
+ Most important -> Local repo config -> Global cli config
55
+ ```
56
+
51
57
  ### Advanced Settings
52
58
 
53
- | Command | Description |
54
- | ------------------------ | --------------------------------------------------------------------------- |
55
- | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
56
- | `glimpse config set-url` | Overrides the default production server with a custom backend or localhost. |
59
+ | Command | Description |
60
+ | ---------------------- | --------------------------------------------------------------------------- |
61
+ | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
62
+ | `glimpse config url` | Overrides the default production server with a custom backend or localhost. |
57
63
 
58
64
  ## Development
59
65
 
@@ -65,7 +71,7 @@ GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.jso
65
71
  source .venv/bin/activate
66
72
  pip install -e .
67
73
  ```
68
- 2. Point the CLI to your local development server:
74
+ 2. Point the CLI to your local development server (use `--local` to not propagate over your real global config):
69
75
  ```bash
70
- glimpse config set-url http://localhost:5000
76
+ glimpse config url http://localhost:5000 --local
71
77
  ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: git-glimpse-cli
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: CLI tool to manage and update shared GitHub repos with GitGlimpse.
5
5
  Author-email: sutaC <contact@sutac.pl>
6
6
  License-Expression: MIT
@@ -65,12 +65,18 @@ pip install git-glimpse-cli
65
65
 
66
66
  GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.json`, and local repository settings are stored at `./.shared-repo.json`.
67
67
 
68
+ Configuration hierarchy:
69
+
70
+ ```
71
+ Most important -> Local repo config -> Global cli config
72
+ ```
73
+
68
74
  ### Advanced Settings
69
75
 
70
- | Command | Description |
71
- | ------------------------ | --------------------------------------------------------------------------- |
72
- | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
73
- | `glimpse config set-url` | Overrides the default production server with a custom backend or localhost. |
76
+ | Command | Description |
77
+ | ---------------------- | --------------------------------------------------------------------------- |
78
+ | `glimpse config debug` | Enables verbose debug mode, printing stack traces. |
79
+ | `glimpse config url` | Overrides the default production server with a custom backend or localhost. |
74
80
 
75
81
  ## Development
76
82
 
@@ -82,7 +88,7 @@ GitGlimpse CLI uses holds your configuration at `~/.config/glimpsecli/config.jso
82
88
  source .venv/bin/activate
83
89
  pip install -e .
84
90
  ```
85
- 2. Point the CLI to your local development server:
91
+ 2. Point the CLI to your local development server (use `--local` to not propagate over your real global config):
86
92
  ```bash
87
- glimpse config set-url http://localhost:5000
93
+ glimpse config url http://localhost:5000 --local
88
94
  ```
@@ -2,10 +2,19 @@ from rich.progress import Progress, SpinnerColumn, TextColumn
2
2
  from glimpsecli import config, helpers
3
3
  from contextlib import nullcontext
4
4
  from json import JSONDecodeError
5
+ from typing import TypedDict
5
6
  from rich import print
6
7
  import typer
7
8
  import httpx
8
9
 
10
+ # --- Responses
11
+ class StatusResponse(TypedDict):
12
+ status: str
13
+ timestamp: str
14
+ code: str | None
15
+ size: str
16
+
17
+ # --- Regests
9
18
  def request_api(
10
19
  endpoint: str,
11
20
  method: str = "GET",
@@ -28,7 +37,7 @@ def request_api(
28
37
  Response from api.
29
38
  """
30
39
  if not token:
31
- token = helpers.get_token()
40
+ token = helpers.fget_token()
32
41
  url = f"{config.get_api_url()}{endpoint}"
33
42
  with Progress(SpinnerColumn(), TextColumn("[progress.description]{task.description}"), transient=True) if not quiet else nullcontext() as progress:
34
43
  if progress:
@@ -52,7 +61,6 @@ def request_api(
52
61
  error = response.json().get("error")
53
62
  if error: print(f"[dim]Error reason: {error}[/dim]")
54
63
  except JSONDecodeError: pass
55
- config.cliconf_save(token="")
56
64
  print("[yellow]Your session has expired. Please run 'glimpse login' again [/yellow].")
57
65
  raise typer.Exit(1)
58
66
  elif response.status_code == 403:
@@ -0,0 +1,126 @@
1
+ from functools import partial
2
+ from typing import TypedDict
3
+ from pathlib import Path
4
+ import json
5
+
6
+ CLI_CONFIG_DIR = Path.home() / ".config" / "glimpsecli"
7
+ CLI_CONFIG_FILE = CLI_CONFIG_DIR / "config.json"
8
+ CONFIG_FILENAME = ".shared-repo.json"
9
+ DEFAULT_API_URL = "https://gitglimpse.sutac.pl"
10
+
11
+ class CliConfig(TypedDict, total=False):
12
+ token: str
13
+ api_url: str
14
+ debug: bool
15
+
16
+ class RepoConfig(CliConfig, total=False):
17
+ repo_id: str
18
+
19
+ # --- repository config
20
+ def _repoconf_load() -> RepoConfig | None:
21
+ """Loads repository config from file.
22
+
23
+ Args:
24
+ path: Path to directory holding config file.
25
+
26
+ Returns:
27
+ RepoConfig if avaliable, else None.
28
+ """
29
+ config_path = Path(".") / CONFIG_FILENAME
30
+ if not config_path.exists():
31
+ return None
32
+ try:
33
+ with open(config_path, "r") as f:
34
+ return json.load(f)
35
+ except Exception:
36
+ return None
37
+
38
+ def repoconf_remove() -> None:
39
+ """Removes repository config file if exists.
40
+
41
+ Args:
42
+ path: Path to directory holding config file.
43
+ """
44
+ config_path = Path(".") / CONFIG_FILENAME
45
+ config_path.unlink(missing_ok=True)
46
+
47
+
48
+ # --- cli config
49
+ def _cliconf_load() -> CliConfig | None:
50
+ """Loads cli configfrom file.
51
+
52
+ Returns:
53
+ CliConfig if avaliable, else None.
54
+ """
55
+ if not CLI_CONFIG_FILE.exists():
56
+ return None
57
+ try:
58
+ with open(CLI_CONFIG_FILE, "r") as f:
59
+ return json.load(f)
60
+ except Exception:
61
+ return None
62
+
63
+ def cliconf_remove() -> None:
64
+ """Removes cli config file."""
65
+ CLI_CONFIG_FILE.unlink(missing_ok=True)
66
+
67
+ # --- conf update
68
+ def conf_save(
69
+ new_conf: CliConfig | RepoConfig | dict,
70
+ local = False
71
+ ) -> None:
72
+ """Save configuration in ethier local or global context.
73
+
74
+ Args:
75
+ local: If True saves config to local context, else saves in global context.
76
+ """
77
+ if local:
78
+ conf = _repoconf_load() or {}
79
+ conf_file = Path(".") / CONFIG_FILENAME
80
+ else:
81
+ CLI_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
82
+ conf = _cliconf_load() or {}
83
+ conf_file = CLI_CONFIG_FILE
84
+ for key, val in new_conf.items():
85
+ if val is not None:
86
+ conf[key] = val
87
+ elif key in conf:
88
+ conf.pop(key)
89
+ with open(conf_file, "w") as f:
90
+ json.dump(conf, f, indent=2)
91
+
92
+ # --- config getters
93
+ def get_api_url() -> str:
94
+ """Returns active api url."""
95
+ if conf := _repoconf_load():
96
+ if user_url := conf.get("api_url"):
97
+ return user_url.rstrip("/")
98
+ if conf := _cliconf_load():
99
+ if user_url := conf.get("api_url"):
100
+ return user_url.rstrip("/")
101
+ return DEFAULT_API_URL
102
+
103
+ def get_debug_mode() -> bool:
104
+ """Returns True if debug is enabled in user config, else False."""
105
+ if conf := _repoconf_load():
106
+ if debug := conf.get("debug") is not None:
107
+ return debug
108
+ if conf := _cliconf_load():
109
+ return conf.get("debug", False)
110
+ return False
111
+
112
+ def get_repo_id() -> str | None:
113
+ """Returns current repo_id."""
114
+ if conf := _repoconf_load():
115
+ return conf.get("repo_id")
116
+ return None
117
+
118
+ def get_token() -> str | None:
119
+ """Returns current user cli token."""
120
+ if conf := _repoconf_load():
121
+ if token := conf.get("token"):
122
+ return token
123
+ if conf := _cliconf_load():
124
+ if token := conf.get("token"):
125
+ return token
126
+ return None
@@ -5,7 +5,6 @@ from rich import print
5
5
  import typer
6
6
  import time
7
7
 
8
-
9
8
  def get_or_prompt_url(url: str | None = None, detect: bool = True) -> str:
10
9
  """Helper to get GitHub repository url.
11
10
 
@@ -38,7 +37,7 @@ def finalize_init(repo_id: str) -> None:
38
37
  repo_id: Repository GitGlimpse id.
39
38
  """
40
39
  # Add repo id to local file
41
- config.repoconf_save(repo_id)
40
+ config.conf_save({"repo_id": repo_id}, local=True)
42
41
  print("[dim]Created local .shared-repo.json configuration file for this repository.[/dim]")
43
42
  # Auto-add to .gitignore
44
43
  gitignore = Path(".gitignore")
@@ -79,19 +78,18 @@ def poll_build_status(token: str, repo_id: str) -> None:
79
78
  print(f"Build has finished with status: {utils.enrich_status(status)}")
80
79
  print(f"View details at: {config.get_api_url()}/repos/details/{repo_id}")
81
80
 
82
- def get_repo_config() -> config.RepoConfig:
81
+ def fget_repo_id() -> str:
83
82
  """Helper for getting repository config."""
84
- conf = config.repoconf_load()
85
- if not conf:
83
+ repo_id = config.get_repo_id()
84
+ if not repo_id:
86
85
  print("[yellow]Not initialised. Run 'glimpse init' first.[/yellow]")
87
86
  raise typer.Exit(1)
88
- return conf
87
+ return repo_id
89
88
 
90
- def get_token() -> str:
89
+ def fget_token() -> str:
91
90
  """Helper for getting api token."""
92
- conf = config.cliconf_load() or {}
93
- token = conf.get("token")
94
- if not conf or not token:
91
+ token = config.get_token()
92
+ if not token:
95
93
  print("[yellow]Not logged in. Run 'glimpse login' first.[/yellow]")
96
94
  raise typer.Exit(code=1)
97
95
  return token
@@ -33,17 +33,19 @@ def login(
33
33
  prompt="Enter your CLI token",
34
34
  hide_input=True,
35
35
  help="Your personal access cli token from dashboard."),
36
- force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing session without prompting.")
36
+ force: bool = typer.Option(False, "--force", "-f", help="Overwrite existing session without prompting."),
37
+ local: bool = typer.Option(False, "--local", "-l", help="Save config to local repository only.")
37
38
  ):
38
39
  """Authenticate cli with server."""
39
- conf = config.cliconf_load()
40
- if not force and conf and conf.get("token"):
40
+ if local: old_token = (config._repoconf_load() or {}).get("token")
41
+ else: old_token = (config._cliconf_load() or {}).get("token")
42
+ if not force and old_token:
41
43
  print("[yellow]You are already logged in.[/yellow]")
42
44
  if not typer.confirm("Do you want to overwrite your existing session?"):
43
45
  print("[dim]Login cancelled.[/dim]")
44
46
  raise typer.Exit(0)
45
47
  response = api.request_api("/cli/user", token=token)
46
- config.cliconf_save(token)
48
+ config.conf_save({"token": token}, local)
47
49
  username = response.json().get("username")
48
50
  print(f"[bold green]Success![/bold green] Authenticated as [cyan]{username}[/cyan].")
49
51
 
@@ -57,13 +59,16 @@ def whoami():
57
59
 
58
60
  # --- logout
59
61
  @app.command()
60
- def logout():
62
+ def logout(
63
+ local: bool = typer.Option(False, "--local", "-l", help="Alter config in local repository only.")
64
+ ):
61
65
  """Clear stored local credentials."""
62
- conf = config.cliconf_load()
63
- if not conf or not conf.get("token"):
66
+ if local: token = (config._repoconf_load() or {}).get("token")
67
+ else: token = (config._cliconf_load() or {}).get("token")
68
+ if not token:
64
69
  print("[yellow]You are not currently logged in.[/yellow]")
65
70
  raise typer.Exit(0)
66
- config.cliconf_save(token="")
71
+ config.conf_save({"token": None})
67
72
  print("[bold green]Logged out successfully.[/bold green]\nStored credentials removed.")
68
73
 
69
74
  # --- init
@@ -76,8 +81,8 @@ def init_link(
76
81
  detect: bool = typer.Option(True, help="Do you want to detect repository url automatically.")
77
82
  ):
78
83
  """Link current Git repository to existing one on GitGlimpse."""
79
- token = helpers.get_token()
80
- if config.repoconf_load():
84
+ token = helpers.fget_token()
85
+ if config.get_repo_id():
81
86
  print("Shared repository already initialised.")
82
87
  raise typer.Exit(0)
83
88
  url = helpers.get_or_prompt_url(url, detect)
@@ -100,8 +105,8 @@ def init_new(
100
105
  detect: bool = typer.Option(True, help="Do you want to detect repository url automatically.")
101
106
  ):
102
107
  """Upload and register current Git repository to GitGlimpse."""
103
- token = helpers.get_token()
104
- if config.repoconf_load():
108
+ token = helpers.fget_token()
109
+ if config.get_repo_id():
105
110
  print("Shared repository already initialised.")
106
111
  raise typer.Exit(0)
107
112
  url = helpers.get_or_prompt_url(url, detect)
@@ -172,14 +177,16 @@ def limits():
172
177
  @app.command()
173
178
  def status():
174
179
  """Display status info of current repository."""
175
- token = helpers.get_token()
176
- conf = helpers.get_repo_config()
177
- response = api.request_api(f"/cli/repos/build/{conf['repo_id']}/status", token=token)
178
- data = response.json()
179
- build_status = data.get("status", "")
180
- print(f"Repository id: [dark_cyan]{conf['repo_id']}[/dark_cyan]")
181
- print(f"Build status: {utils.enrich_status(build_status)}")
182
- print(f"Repository details: {config.get_api_url()}/repos/details/{conf['repo_id']}")
180
+ token = helpers.fget_token()
181
+ repo_id = helpers.fget_repo_id()
182
+ response = api.request_api(f"/cli/repos/status/{repo_id}", token=token)
183
+ data: api.StatusResponse = response.json()
184
+ print(f"Repository id: [dark_cyan]{repo_id}[/dark_cyan]")
185
+ print(f"Build status: {utils.enrich_status(data.get('status'))}")
186
+ if data.get("code"): print(f"Build code: [dark_red]{data['code']}[/dark_red]")
187
+ print(f"Build timestamp: [not bold dark_blue]{data.get('timestamp', '?')}[/not bold dark_blue]")
188
+ print(f"Build size: [not bold dark_blue]{data.get('size', '?')}[/not bold dark_blue]")
189
+ print(f"Repository details: {config.get_api_url()}/repos/details/{repo_id}")
183
190
 
184
191
  # --- remove
185
192
  @app.command()
@@ -187,11 +194,11 @@ def remove(
187
194
  force: bool = typer.Option(False, "--force", "-f", help="Remove without prompting.")
188
195
  ):
189
196
  """Remove current repository from GitGlimpse."""
190
- token = helpers.get_token()
191
- conf = helpers.get_repo_config()
192
- print(f"[dim]Current repository id: '{conf['repo_id']}'[/dim]")
197
+ token = helpers.fget_token()
198
+ repo_id = helpers.fget_repo_id()
199
+ print(f"[dim]Current repository id: '{repo_id}'[/dim]")
193
200
  if not force: typer.confirm(f"Are you sure, you want to remove this repository from GitGlimpse?", abort=True)
194
- api.request_api(f"/cli/repos/remove/{conf['repo_id']}", method="POST", token=token, handle_codes=[204])
201
+ api.request_api(f"/cli/repos/remove/{repo_id}", method="POST", token=token, handle_codes=[204])
195
202
  config.repoconf_remove()
196
203
  print("Removed repository from GitGlimpse.")
197
204
 
@@ -201,13 +208,13 @@ def build(
201
208
  force: bool = typer.Option(False, "--force", "-f", help="Schedule without prompting.")
202
209
  ):
203
210
  """Schedule a build for current repository."""
204
- token = helpers.get_token()
205
- conf = helpers.get_repo_config()
211
+ token = helpers.fget_token()
212
+ repo_id = helpers.fget_repo_id()
206
213
  if not force:
207
214
  print("Ensure that you pushed recent changes to GitHub.")
208
215
  typer.confirm("Continue?", abort=True)
209
- print(f"[dim]Scheduling a build for current repository (id: '{conf['repo_id']}')[/dim]")
210
- response = api.request_api(f"/cli/repos/build/{conf['repo_id']}", method="POST", token=token, handle_codes=[202, 420, 425])
216
+ print(f"[dim]Scheduling a build for current repository (id: '{repo_id}')[/dim]")
217
+ response = api.request_api(f"/cli/repos/build/{repo_id}", method="POST", token=token, handle_codes=[202, 420, 425])
211
218
  if response.status_code == 420:
212
219
  print(f"[yellow]You have reached your usage limits.[/yellow]")
213
220
  try:
@@ -219,36 +226,56 @@ def build(
219
226
  print(f"[yellow]This repository already has a pending build.[/yellow]")
220
227
  raise typer.Exit(1)
221
228
  # response.status_code == 202
222
- helpers.poll_build_status(token, conf['repo_id'])
229
+ helpers.poll_build_status(token, repo_id)
223
230
 
224
231
  # --- config
225
232
  config_app = typer.Typer(help="Configure your cli.", no_args_is_help=True)
226
233
  app.add_typer(config_app, name="config")
227
234
 
228
- @config_app.command("set-url")
229
- def config_set_url(
230
- url: str = typer.Argument(..., help="Api base url (e.g., http://localhost:8000 or https://gitglimpse.sutac.pl)")
235
+ @config_app.command("list")
236
+ def config_list(
237
+ sensitive: bool = typer.Option(False, "--show-sensitive", help="Display sensitive data."),
238
+ local: bool = typer.Option(None, "--local/--global", help="Display only local or global config.")
239
+ ):
240
+ """Get current cli config."""
241
+ sensitive_keys = ["token", "repo_id"]
242
+ lconf = config._repoconf_load() or {}
243
+ gconf = config._cliconf_load() or {}
244
+ if local is None:
245
+ print("[bold]Current cli config[/bold]")
246
+ elif local:
247
+ print("[bold]Current LOCAL cli config[/bold]")
248
+ else:
249
+ print("[bold]Current GLOBAL cli config[/bold]")
250
+ if local != False:
251
+ for key, val in lconf.items():
252
+ val = f"[dim][{key.upper()}][/dim]" if not sensitive and key in sensitive_keys else val
253
+ print(f"{key}: {val}")
254
+ if local != True:
255
+ for key, val in gconf.items():
256
+ if local is None and key in lconf: continue
257
+ val = f"[dim][{key.upper()}][/dim]" if not sensitive and key in sensitive_keys else val
258
+ print(f"{key}: {val}")
259
+
260
+ @config_app.command("url")
261
+ def config_url(
262
+ url: str = typer.Argument(..., help="Api base url (e.g., http://localhost:8000 or https://gitglimpse.sutac.pl)"),
263
+ local: bool = typer.Option(False, "--local", "-l", help="Save config to local repository only.")
231
264
  ):
232
265
  """Set the active api server url."""
233
- config.cliconf_save(api_url=url)
266
+ config.conf_save({"api_url": (url if url else None)}, local)
234
267
  if url:
235
268
  print(f"Api url updated to: {url}")
236
269
  else:
237
270
  print("Api url unset.")
238
271
 
239
- @config_app.command("get-url")
240
- def config_get_url():
241
- """Get the active api server url."""
242
- conf = config.cliconf_load() or {}
243
- url = conf.get("api_url")
244
- if url: print(url)
245
-
246
272
  @config_app.command("debug")
247
273
  def config_debug(
248
- enabled: bool = typer.Argument(..., help="True to enable debug mode, False to disable.")
249
- ):
274
+ enabled: bool = typer.Argument(..., help="True to enable debug mode, False to disable."),
275
+ local: bool = typer.Option(False, "--local", "-l", help="Save config to local repository only.")
276
+ ):
250
277
  """Enable or disable verbose debug output and full crash stack traces."""
251
- config.cliconf_save(debug=enabled)
278
+ config.conf_save({"debug": enabled}, local)
252
279
  status = "[bold green]enabled[/bold green]" if enabled else "[bold yellow]disabled[/bold yellow]"
253
280
  print(f"Debug mode {status}.")
254
281
 
@@ -7,7 +7,7 @@ name = "git-glimpse-cli"
7
7
  authors = [
8
8
  { name="sutaC", email="contact@sutac.pl" },
9
9
  ]
10
- version = "0.1.1"
10
+ version = "0.1.3"
11
11
  description = "CLI tool to manage and update shared GitHub repos with GitGlimpse."
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.10"
@@ -1,116 +0,0 @@
1
- from typing import TypedDict
2
- from pathlib import Path
3
- import json
4
- import os
5
-
6
- CLI_CONFIG_DIR = Path.home() / ".config" / "glimpsecli"
7
- CLI_CONFIG_FILE = CLI_CONFIG_DIR / "config.json"
8
- CONFIG_FILENAME = ".shared-repo.json"
9
- DEFAULT_API_URL = "https://gitglimpse.sutac.pl"
10
-
11
- # --- repository config
12
- class RepoConfig(TypedDict):
13
- repo_id: str
14
-
15
- def repoconf_save(repo_id: str, path: Path = Path(".")) -> None:
16
- """Saves repository config to file.
17
-
18
- Args:
19
- repo_id: Repository GitGlimpse id.
20
- path: Path to directory holding config file.
21
- """
22
- config_path = path / CONFIG_FILENAME
23
- conf: RepoConfig = {"repo_id": repo_id}
24
- with open(config_path, "w") as f:
25
- json.dump(conf, f, indent=2)
26
-
27
- def repoconf_load(path: Path = Path(".")) -> RepoConfig | None:
28
- """Loads repository config from file.
29
-
30
- Args:
31
- path: Path to directory holding config file.
32
-
33
- Returns:
34
- RepoConfig if avaliable, else None.
35
- """
36
- config_path = path / CONFIG_FILENAME
37
- if not config_path.exists():
38
- return None
39
- try:
40
- with open(config_path, "r") as f:
41
- return json.load(f)
42
- except Exception:
43
- return None
44
-
45
- def repoconf_remove(path: Path = Path(".")) -> None:
46
- """Removes repository config file if exists.
47
-
48
- Args:
49
- path: Path to directory holding config file.
50
- """
51
- config_path = path / CONFIG_FILENAME
52
- config_path.unlink(missing_ok=True)
53
-
54
- # --- cli config
55
- class CliConfig(TypedDict, total=False):
56
- token: str
57
- api_url: str
58
- debug: bool
59
-
60
- def cliconf_save(
61
- token: str | None = None,
62
- api_url: str | None = None,
63
- debug: bool | None = None
64
- ) -> None:
65
- """Saves or updates cli config safely.
66
-
67
- Args:
68
- token: GitGlimpse api token.
69
- """
70
- CLI_CONFIG_DIR.mkdir(parents=True, exist_ok=True)
71
- conf = cliconf_load() or {}
72
- if token is not None:
73
- if token: conf["token"] = token
74
- elif "token" in conf: conf.pop("token")
75
- if api_url is not None:
76
- if api_url: conf["api_url"] = api_url.rstrip("/")
77
- elif "api_url" in conf: conf.pop("api_url")
78
- if debug is not None:
79
- conf["debug"] = debug
80
- with open(CLI_CONFIG_FILE, "w") as f:
81
- json.dump(conf, f, indent=2)
82
-
83
- def cliconf_load() -> CliConfig | None:
84
- """Loads cli configfrom file.
85
-
86
- Returns:
87
- CliConfig if avaliable, else None.
88
- """
89
- if not CLI_CONFIG_FILE.exists():
90
- return None
91
- try:
92
- with open(CLI_CONFIG_FILE, "r") as f:
93
- return json.load(f)
94
- except Exception:
95
- return None
96
-
97
- def cliconf_remove() -> None:
98
- """Removes cli config file."""
99
- CLI_CONFIG_FILE.unlink(missing_ok=True)
100
-
101
- def get_api_url() -> str:
102
- """Resolves active api url
103
-
104
- Returns:
105
- Active api url.
106
- """
107
- if conf := cliconf_load():
108
- if user_url := conf.get("api_url"):
109
- return user_url.rstrip("/")
110
- return DEFAULT_API_URL
111
-
112
- def get_debug_mode() -> bool:
113
- """Returns True if debug is enabled in user config, else False."""
114
- if conf := cliconf_load():
115
- return conf.get("debug", False)
116
- return False
File without changes