steve-cli 0.3.1__tar.gz → 0.3.2__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.
- {steve_cli-0.3.1 → steve_cli-0.3.2}/PKG-INFO +1 -1
- {steve_cli-0.3.1 → steve_cli-0.3.2}/pyproject.toml +1 -1
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli/cli.py +95 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli/storage.py +8 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/PKG-INFO +1 -1
- {steve_cli-0.3.1 → steve_cli-0.3.2}/README.md +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/setup.cfg +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli/__init__.py +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/SOURCES.txt +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/dependency_links.txt +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/entry_points.txt +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/requires.txt +0 -0
- {steve_cli-0.3.1 → steve_cli-0.3.2}/steve_cli.egg-info/top_level.txt +0 -0
|
@@ -233,5 +233,100 @@ def setup_env():
|
|
|
233
233
|
click.echo(f" 💡 Run: {click.style(f'source {out_file.name}', fg='yellow')}")
|
|
234
234
|
|
|
235
235
|
|
|
236
|
+
def _detect_workspaces() -> List[str]:
|
|
237
|
+
tiers = {"BRONZE", "SILVER", "GOLD"}
|
|
238
|
+
workspaces = set()
|
|
239
|
+
for key in os.environ:
|
|
240
|
+
if key.endswith("_ACCESS_KEY"):
|
|
241
|
+
prefix = key[: -len("_ACCESS_KEY")]
|
|
242
|
+
if prefix not in tiers:
|
|
243
|
+
workspaces.add(prefix)
|
|
244
|
+
return sorted(workspaces)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _build_tree(keys: List[str]) -> Dict[str, Any]:
|
|
248
|
+
tree: Dict[str, Any] = {}
|
|
249
|
+
for key in keys:
|
|
250
|
+
parts = key.split("/")
|
|
251
|
+
node = tree
|
|
252
|
+
for part in parts[:-1]:
|
|
253
|
+
node = node.setdefault(part, {})
|
|
254
|
+
node[parts[-1]] = None
|
|
255
|
+
return tree
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _print_tree(node: Dict[str, Any], prefix: str = "", is_last: bool = True) -> None:
|
|
259
|
+
items = sorted(node.items())
|
|
260
|
+
for i, (name, child) in enumerate(items):
|
|
261
|
+
last = i == len(items) - 1
|
|
262
|
+
connector = "└── " if last else "├── "
|
|
263
|
+
click.echo(f"{prefix}{connector}{name}")
|
|
264
|
+
if child is not None:
|
|
265
|
+
extension = " " if last else "│ "
|
|
266
|
+
_print_tree(child, prefix + extension, last)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
def _list_bucket(storage_kwargs: dict, label: str, bucket_name: str) -> None:
|
|
270
|
+
click.echo(f" {click.style(label, fg='cyan')} ({bucket_name})")
|
|
271
|
+
try:
|
|
272
|
+
from steve_cli.storage import S3Storage
|
|
273
|
+
storage = S3Storage(**storage_kwargs)
|
|
274
|
+
keys = storage.list_all()
|
|
275
|
+
if not keys:
|
|
276
|
+
click.echo(" (empty)")
|
|
277
|
+
else:
|
|
278
|
+
tree = _build_tree(keys)
|
|
279
|
+
_print_tree(tree, prefix=" ")
|
|
280
|
+
except EnvironmentError as e:
|
|
281
|
+
click.echo(f" ⚠️ {e}", err=True)
|
|
282
|
+
except Exception as e:
|
|
283
|
+
click.echo(f" ❌ {e}", err=True)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
@main.command("buckets")
|
|
287
|
+
def buckets():
|
|
288
|
+
"""List all S3 buckets detected from env variables and show their files as a tree."""
|
|
289
|
+
tiers = ["bronze", "silver", "gold"]
|
|
290
|
+
|
|
291
|
+
options: List[Dict[str, Any]] = []
|
|
292
|
+
|
|
293
|
+
bare_tiers = [t for t in tiers if os.getenv(f"{t.upper()}_ACCESS_KEY")]
|
|
294
|
+
for tier in bare_tiers:
|
|
295
|
+
bucket_name = os.getenv(f"{tier.upper()}_BUCKET", "")
|
|
296
|
+
options.append({
|
|
297
|
+
"label": f"default / {tier} ({bucket_name})",
|
|
298
|
+
"kwargs": {"tier": tier},
|
|
299
|
+
"bucket_name": bucket_name,
|
|
300
|
+
"tier": tier,
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
for ws in _detect_workspaces():
|
|
304
|
+
for tier in tiers:
|
|
305
|
+
bucket_name = os.getenv(f"{ws}_BUCKET_{tier.upper()}")
|
|
306
|
+
if not bucket_name:
|
|
307
|
+
continue
|
|
308
|
+
options.append({
|
|
309
|
+
"label": f"{ws} / {tier} ({bucket_name})",
|
|
310
|
+
"kwargs": {"tier": tier, "workspace": ws.lower().replace("_", "-")},
|
|
311
|
+
"bucket_name": bucket_name,
|
|
312
|
+
"tier": tier,
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
if not options:
|
|
316
|
+
click.echo("No bucket env variables found (expected: BRONZE_ACCESS_KEY or {WORKSPACE}_ACCESS_KEY).")
|
|
317
|
+
return
|
|
318
|
+
|
|
319
|
+
choice = questionary.select(
|
|
320
|
+
"Select a bucket to list:",
|
|
321
|
+
choices=[o["label"] for o in options],
|
|
322
|
+
).ask()
|
|
323
|
+
|
|
324
|
+
if choice is None:
|
|
325
|
+
sys.exit(0)
|
|
326
|
+
|
|
327
|
+
selected = next(o for o in options if o["label"] == choice)
|
|
328
|
+
_list_bucket(selected["kwargs"], selected["tier"], selected["bucket_name"])
|
|
329
|
+
|
|
330
|
+
|
|
236
331
|
if __name__ == '__main__':
|
|
237
332
|
main()
|
|
@@ -161,6 +161,14 @@ class S3Storage:
|
|
|
161
161
|
for obj in resp.get("Contents", [])
|
|
162
162
|
]
|
|
163
163
|
|
|
164
|
+
def list_all(self) -> List[str]:
|
|
165
|
+
keys = []
|
|
166
|
+
paginator = self.client.get_paginator("list_objects_v2")
|
|
167
|
+
for page in paginator.paginate(Bucket=self.bucket):
|
|
168
|
+
for obj in page.get("Contents", []):
|
|
169
|
+
keys.append(obj["Key"])
|
|
170
|
+
return keys
|
|
171
|
+
|
|
164
172
|
|
|
165
173
|
def get_storage(tier: str = "bronze", workspace: str | None = None) -> Storage:
|
|
166
174
|
return S3Storage(tier=tier, workspace=workspace)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|