sweatstack-cli 0.7.2__py3-none-any.whl → 0.8.0__py3-none-any.whl

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,3 +1,3 @@
1
1
  """SweatStack CLI — Command-line interface for the SweatStack platform."""
2
2
 
3
- __version__ = "0.7.2"
3
+ __version__ = "0.8.0"
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json as json_lib
6
+ import sys
6
7
  from pathlib import Path
7
8
  from typing import Any, cast
8
9
 
@@ -56,7 +57,7 @@ def create(
56
57
  """Create a new private OAuth2 application."""
57
58
  if env and env_file:
58
59
  console.print("[red]Error:[/red] --env and --env-file are mutually exclusive.")
59
- raise typer.Exit(4)
60
+ raise typer.Exit(1)
60
61
 
61
62
  body: dict[str, str | bool] = {"name": name}
62
63
 
@@ -86,7 +87,18 @@ def create(
86
87
  if env_path:
87
88
  _write_env_file(env_path, result)
88
89
 
89
- # Write sweatstack.toml
90
+ # Write sweatstack.toml (protect existing file)
91
+ config_path = Path(CONFIG_FILENAME)
92
+ if config_path.exists():
93
+ if not sys.stdin.isatty():
94
+ console.print(
95
+ f"[red]Error:[/red] {CONFIG_FILENAME} already exists. "
96
+ "Remove it first or run interactively."
97
+ )
98
+ raise typer.Exit(1)
99
+ console.print(f"[yellow]Warning:[/yellow] {CONFIG_FILENAME} already exists.")
100
+ typer.confirm("Overwrite?", abort=True)
101
+
90
102
  config_path = write_config(
91
103
  app_name=result["name"],
92
104
  client_id=result["id"],
@@ -206,6 +218,9 @@ def link(
206
218
  console.print(f" [cyan][{i}][/cyan] {app_info['name']}{page_info}")
207
219
 
208
220
  console.print()
221
+ if not sys.stdin.isatty():
222
+ console.print("[red]Error:[/red] Cannot select application in non-interactive mode.")
223
+ raise typer.Exit(1)
209
224
  selection = typer.prompt("Select application", type=int)
210
225
 
211
226
  if selection < 1 or selection > len(apps):
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import mimetypes
6
+ import sys
6
7
  from pathlib import Path
7
8
 
8
9
  import typer
@@ -12,7 +13,7 @@ from sweatstack_cli.api import APIClient
12
13
  from sweatstack_cli.config import get_settings
13
14
  from sweatstack_cli.console import console
14
15
  from sweatstack_cli.exceptions import APIError, AuthenticationError
15
- from sweatstack_cli.project import CONFIG_FILENAME, ProjectConfig
16
+ from sweatstack_cli.project import CONFIG_FILENAME, ProjectConfig, update_config, write_config
16
17
 
17
18
  # URL where users can create pages for their applications
18
19
  SETTINGS_URL = "https://app.sweatstack.no/settings/api"
@@ -59,12 +60,14 @@ def deploy(
59
60
  None,
60
61
  help="Page slug. Reads from sweatstack.toml if not provided.",
61
62
  ),
62
- directory: Path | None = typer.Argument(
63
+ directory: Path | None = typer.Option(
63
64
  None,
64
- help="Directory to deploy. Reads from sweatstack.toml or defaults to current dir.",
65
+ "--dir",
66
+ help="Directory to deploy. Reads from sweatstack.toml if not provided.",
65
67
  ),
66
68
  ) -> None:
67
69
  """Deploy a static site to SweatStack Pages."""
70
+
68
71
  # Load config for defaults
69
72
  config = ProjectConfig.load()
70
73
 
@@ -76,9 +79,19 @@ def deploy(
76
79
  raise typer.Exit(1)
77
80
 
78
81
  # Resolve directory
82
+ directory_explicit = directory is not None
79
83
  if directory is None:
80
84
  dir_from_config = config.page.directory if config else None
81
- directory = Path(dir_from_config) if dir_from_config else Path(".")
85
+ if dir_from_config:
86
+ directory = Path(dir_from_config)
87
+ else:
88
+ if not sys.stdin.isatty():
89
+ console.print("[red]Error:[/red] No deploy directory specified.")
90
+ console.print("Use --dir or set it in sweatstack.toml.")
91
+ raise typer.Exit(1)
92
+ dir_input = typer.prompt("Deploy directory", default="public/")
93
+ directory = Path(dir_input)
94
+ directory_explicit = True
82
95
 
83
96
  if not directory.exists():
84
97
  console.print(f"[red]Error:[/red] Directory '{directory}' does not exist.")
@@ -90,6 +103,32 @@ def deploy(
90
103
 
91
104
  directory = directory.resolve()
92
105
 
106
+ # Compute relative directory for config and warnings
107
+ try:
108
+ rel_dir = str(directory.relative_to(Path.cwd()))
109
+ except ValueError:
110
+ rel_dir = str(directory)
111
+
112
+ # Warn and confirm if deploying with different values than toml
113
+ if config:
114
+ mismatches: list[str] = []
115
+ if config.page.slug and slug and slug != config.page.slug:
116
+ mismatches.append(
117
+ f"Deploying to '{slug}' but "
118
+ f"{CONFIG_FILENAME} has slug '{config.page.slug}'"
119
+ )
120
+ if config.page.directory and rel_dir != config.page.directory:
121
+ mismatches.append(
122
+ f"Deploying '{rel_dir}' but "
123
+ f"{CONFIG_FILENAME} has directory '{config.page.directory}'"
124
+ )
125
+ if mismatches:
126
+ for msg in mismatches:
127
+ console.print(f"[yellow]Warning:[/yellow] {msg}")
128
+ if not sys.stdin.isatty():
129
+ raise typer.Exit(1)
130
+ typer.confirm("Continue?", abort=True)
131
+
93
132
  # Collect files
94
133
  files = _collect_files(directory)
95
134
  file_count = len(files)
@@ -143,6 +182,20 @@ def deploy(
143
182
  console.print(f"[red]Error:[/red] {e}")
144
183
  raise typer.Exit(3)
145
184
 
185
+ # Save to config after successful deploy
186
+ if config:
187
+ update_kwargs: dict[str, str] = {}
188
+ if not config.page.slug:
189
+ update_kwargs["page_slug"] = page_slug
190
+ if not config.page.directory and directory_explicit:
191
+ update_kwargs["page_directory"] = rel_dir
192
+ if update_kwargs:
193
+ update_config(**update_kwargs)
194
+ console.print(f"[green]✓[/green] Saved to [bold]{CONFIG_FILENAME}[/bold]")
195
+ elif directory_explicit:
196
+ write_config(page_slug=page_slug, page_directory=rel_dir)
197
+ console.print(f"[green]✓[/green] Created [bold]{CONFIG_FILENAME}[/bold]")
198
+
146
199
  console.print()
147
200
  console.print(f"[green]✓[/green] Deployed [bold]{page_slug}[/bold] successfully!")
148
201
  console.print(f" Files: {file_count}")
sweatstack_cli/project.py CHANGED
@@ -109,3 +109,37 @@ def write_config(
109
109
  config_path.write_text("\n".join(lines), encoding="utf-8")
110
110
 
111
111
  return config_path
112
+
113
+
114
+ def update_config(
115
+ path: Path | None = None,
116
+ *,
117
+ page_slug: str | None = None,
118
+ page_directory: str | None = None,
119
+ ) -> Path | None:
120
+ """Update specific fields in an existing sweatstack.toml without overwriting other values."""
121
+ config_path = path or Path(CONFIG_FILENAME)
122
+
123
+ if not config_path.exists():
124
+ return None
125
+
126
+ try:
127
+ data = tomllib.loads(config_path.read_text(encoding="utf-8"))
128
+ except tomllib.TOMLDecodeError:
129
+ return None
130
+
131
+ app_data = data.get("app", {})
132
+ page_data = data.get("page", {})
133
+
134
+ if page_slug is not None:
135
+ page_data["slug"] = page_slug
136
+ if page_directory is not None:
137
+ page_data["directory"] = page_directory
138
+
139
+ return write_config(
140
+ path=config_path,
141
+ app_name=app_data.get("name"),
142
+ client_id=app_data.get("client_id"),
143
+ page_slug=page_data.get("slug"),
144
+ page_directory=page_data.get("directory"),
145
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sweatstack-cli
3
- Version: 0.7.2
3
+ Version: 0.8.0
4
4
  Summary: Command-line interface for SweatStack — the sports data platform for developers
5
5
  Project-URL: Homepage, https://sweatstack.no
6
6
  Project-URL: Documentation, https://docs.sweatstack.no
@@ -130,8 +130,8 @@ Deploy static sites to SweatStack Pages.
130
130
  # Deploy using sweatstack.toml defaults (recommended)
131
131
  sweatstack page deploy
132
132
 
133
- # Or specify slug and directory directly
134
- sweatstack page deploy my-page ./dist
133
+ # Or specify slug and directory explicitly
134
+ sweatstack page deploy my-page --dir ./dist
135
135
  ```
136
136
 
137
137
  ## License
@@ -1,9 +1,9 @@
1
- sweatstack_cli/__init__.py,sha256=ZNIAY7QNAqi01JDEnHZN9bJyiM_TICftFpoY4fNRsZ8,100
1
+ sweatstack_cli/__init__.py,sha256=PrOyFimElYJM3lDfkHlz_9FFLf3CN85a82pavfqBUgo,100
2
2
  sweatstack_cli/config.py,sha256=cD4OXnVAKVagYazyk9Cw-WWx62tJPqcjQt1cknYyymM,706
3
3
  sweatstack_cli/console.py,sha256=69KWAM7ksnYRTONMYTXGXN4LWRAQhRxcLXO0LcWGLqo,300
4
4
  sweatstack_cli/exceptions.py,sha256=qEUu8GydXGRKR4ndAdZ2wnkHekmFpBXDJiT5bXdl7Yk,613
5
5
  sweatstack_cli/main.py,sha256=obcF1VGhwcE_pI1BwTJkY4J4BgIGac-y3yYGvY6ZOp8,2330
6
- sweatstack_cli/project.py,sha256=mOwqzuc6D6J6IHf-5L9Vv_sfjpReS4vwzSjUSFi7qys,2716
6
+ sweatstack_cli/project.py,sha256=yUpkrEbPD2c0lu8IKw88aQrkFtksLHMjmgHlaqKVQUo,3660
7
7
  sweatstack_cli/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  sweatstack_cli/version_check.py,sha256=yZ_qVe7tUBowUDAmMki-wyf5pSxlNDsoWKgTcSfesBA,2015
9
9
  sweatstack_cli/api/__init__.py,sha256=l9s6BzF6Wx17K_ElRUX-8aAwwIIzqmGF1PQ3Mbc07mw,107
@@ -14,10 +14,10 @@ sweatstack_cli/auth/jwt.py,sha256=Ja6ccFyRKtX5MPjFdHNavVYf8KTz0MYT86StbnF-cwg,15
14
14
  sweatstack_cli/auth/pkce.py,sha256=sO_xUeN1UIvXKX0yEIGgMTIIzvfCOCUwlyNAveEd16k,1542
15
15
  sweatstack_cli/auth/tokens.py,sha256=kc8ytoO9PAyjLD6wUTIagVuP7_ia5ZjW5m_WjXIRIkA,4942
16
16
  sweatstack_cli/commands/__init__.py,sha256=YCO8wYVvdGeoEI0p_oUm8ANLneuET4SHsKDjitMjAgY,35
17
- sweatstack_cli/commands/app.py,sha256=pk6QBUF0yQcrLJYR47TniMVhl-5FNeQopQeV3BTop0k,6924
17
+ sweatstack_cli/commands/app.py,sha256=dsGfNm2xPk1VV31RGD5N58LnG5__y902F2-o2uq5BeI,7552
18
18
  sweatstack_cli/commands/auth.py,sha256=lmlN2RcZIHdbZG-EEUVFwOSn6vPN-Ijgh_5E-cFQVtU,3478
19
- sweatstack_cli/commands/page.py,sha256=tEWjzOmht_IKm47lbprJz-XDKibrx5nFlStuvBRST6A,5329
20
- sweatstack_cli-0.7.2.dist-info/METADATA,sha256=WI1UvMTUMV02t4lSThgQR6uh5WpY2tm21oRBhndx6nQ,3664
21
- sweatstack_cli-0.7.2.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
22
- sweatstack_cli-0.7.2.dist-info/entry_points.txt,sha256=rnH0EV1U9CQlmKQcJ7RmxGpNRTa9J-YdiIJZDYrAXb4,56
23
- sweatstack_cli-0.7.2.dist-info/RECORD,,
19
+ sweatstack_cli/commands/page.py,sha256=aiuKVYUBdV2AcAcDAISx8qzs0PKieXQKAoYq00fTDew,7470
20
+ sweatstack_cli-0.8.0.dist-info/METADATA,sha256=XNNUhVP7eyRvhKCKEyk7eCNkIpZ-PDYjrEeWmTb8lZs,3672
21
+ sweatstack_cli-0.8.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
22
+ sweatstack_cli-0.8.0.dist-info/entry_points.txt,sha256=rnH0EV1U9CQlmKQcJ7RmxGpNRTa9J-YdiIJZDYrAXb4,56
23
+ sweatstack_cli-0.8.0.dist-info/RECORD,,