dayhoff-tools 1.0.1__py3-none-any.whl → 1.0.3__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.
@@ -0,0 +1,597 @@
1
+ """CLI commands for cloud provider authentication and management.
2
+
3
+ This module provides commands for authenticating with GCP and AWS from within
4
+ development containers. It handles both immediate shell environment configuration
5
+ via the --export flag and persistent configuration via shell RC files.
6
+
7
+ The implementation focuses on:
8
+ 1. Unifying cloud authentication with the `dh` CLI tool
9
+ 2. Maintaining persistence across shell sessions via RC file modifications
10
+ 3. Providing similar capabilities to the shell scripts it replaces
11
+ """
12
+
13
+ import os
14
+ import re
15
+ import shutil
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+ from typing import List, Optional, Tuple
20
+
21
+ import questionary
22
+ import typer
23
+
24
+ # --- Configuration ---
25
+ GCP_DEVCON_SA = "devcon@enzyme-discovery.iam.gserviceaccount.com"
26
+ GCP_PROJECT_ID = "enzyme-discovery"
27
+ AWS_DEFAULT_PROFILE = "dev-devaccess"
28
+ AWS_CONFIG_FILE = Path.home() / ".aws" / "config"
29
+ SHELL_RC_FILES = [
30
+ Path.home() / ".bashrc",
31
+ Path.home() / ".bash_profile",
32
+ Path.home() / ".profile",
33
+ ]
34
+
35
+ # --- Color constants for formatted output ---
36
+ RED = "\033[0;31m"
37
+ GREEN = "\033[0;32m"
38
+ YELLOW = "\033[0;33m"
39
+ BLUE = "\033[0;36m"
40
+ NC = "\033[0m" # No Color
41
+
42
+
43
+ # --- Common Helper Functions ---
44
+ def _find_executable(name: str) -> str:
45
+ """Find the full path to an executable in PATH."""
46
+ path = shutil.which(name)
47
+ if not path:
48
+ raise FileNotFoundError(
49
+ f"{name} command not found. Please ensure it's installed."
50
+ )
51
+ return path
52
+
53
+
54
+ def _run_command(
55
+ cmd_list: List[str],
56
+ capture: bool = False,
57
+ check: bool = True,
58
+ suppress_output: bool = False,
59
+ ) -> Tuple[int, str, str]:
60
+ """Run a command and return its result.
61
+
62
+ Args:
63
+ cmd_list: List of command arguments
64
+ capture: Whether to capture output
65
+ check: Whether to raise on non-zero exit code
66
+ suppress_output: Whether to hide output even if not captured
67
+
68
+ Returns:
69
+ Tuple of (return_code, stdout_str, stderr_str)
70
+ """
71
+ stdout_opt = (
72
+ subprocess.PIPE if capture else subprocess.DEVNULL if suppress_output else None
73
+ )
74
+ stderr_opt = (
75
+ subprocess.PIPE if capture else subprocess.DEVNULL if suppress_output else None
76
+ )
77
+
78
+ try:
79
+ result = subprocess.run(
80
+ cmd_list, stdout=stdout_opt, stderr=stderr_opt, check=check, text=True
81
+ )
82
+ return (
83
+ result.returncode,
84
+ result.stdout if capture else "",
85
+ result.stderr if capture else "",
86
+ )
87
+ except subprocess.CalledProcessError as e:
88
+ if capture:
89
+ return (e.returncode, e.stdout or "", e.stderr or "")
90
+ return (e.returncode, "", "")
91
+
92
+
93
+ def _modify_rc_files(variable: str, value: Optional[str]) -> None:
94
+ """Add or remove an export line from RC files.
95
+
96
+ Args:
97
+ variable: Environment variable name
98
+ value: Value to set, or None to remove
99
+ """
100
+ for rc_file in SHELL_RC_FILES:
101
+ if not rc_file.exists():
102
+ continue
103
+
104
+ try:
105
+ # Read existing content
106
+ with open(rc_file, "r") as f:
107
+ lines = f.readlines()
108
+
109
+ # Filter out existing exports for this variable
110
+ pattern = re.compile(f"^export {variable}=")
111
+ new_lines = [line for line in lines if not pattern.match(line.strip())]
112
+
113
+ # Add new export if value is provided
114
+ if value is not None:
115
+ new_lines.append(f"export {variable}={value}\n")
116
+
117
+ # Write back to file
118
+ with open(rc_file, "w") as f:
119
+ f.writelines(new_lines)
120
+
121
+ except (IOError, PermissionError) as e:
122
+ print(f"Warning: Could not update {rc_file}: {e}", file=sys.stderr)
123
+
124
+
125
+ def _get_env_var(variable: str) -> Optional[str]:
126
+ """Safely get an environment variable."""
127
+ return os.environ.get(variable)
128
+
129
+
130
+ # --- GCP Functions ---
131
+ def _is_gcp_user_authenticated() -> bool:
132
+ """Check if a user is authenticated with GCP (not a compute service account)."""
133
+ gcloud_path = _find_executable("gcloud")
134
+ cmd = [
135
+ gcloud_path,
136
+ "auth",
137
+ "list",
138
+ "--filter=status:ACTIVE",
139
+ "--format=value(account)",
140
+ ]
141
+ _, stdout, _ = _run_command(cmd, capture=True, check=False)
142
+
143
+ account = stdout.strip()
144
+ return bool(account) and "compute@developer.gserviceaccount.com" not in account
145
+
146
+
147
+ def _get_current_gcp_user() -> str:
148
+ """Get the currently authenticated GCP user."""
149
+ gcloud_path = _find_executable("gcloud")
150
+ cmd = [
151
+ gcloud_path,
152
+ "auth",
153
+ "list",
154
+ "--filter=status:ACTIVE",
155
+ "--format=value(account)",
156
+ ]
157
+ _, stdout, _ = _run_command(cmd, capture=True, check=False)
158
+
159
+ account = stdout.strip()
160
+ if account:
161
+ if "compute@developer.gserviceaccount.com" in account:
162
+ return "Not authenticated (using VM service account)"
163
+ return account
164
+ return "Not authenticated"
165
+
166
+
167
+ def _get_current_gcp_impersonation() -> str:
168
+ """Get the current impersonated service account, if any."""
169
+ sa = _get_env_var("CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT")
170
+ return sa if sa else "None"
171
+
172
+
173
+ def _run_gcloud_login() -> None:
174
+ """Run the gcloud auth login command."""
175
+ gcloud_path = _find_executable("gcloud")
176
+ print(f"{BLUE}Authenticating with Google Cloud...{NC}")
177
+ _run_command([gcloud_path, "auth", "login"])
178
+ print(f"{GREEN}Authentication complete.{NC}")
179
+
180
+
181
+ def _test_gcp_credentials(user: str, impersonation_sa: str) -> None:
182
+ """Test GCP credentials with and without impersonation."""
183
+ gcloud_path = _find_executable("gcloud")
184
+
185
+ print(f"\n{BLUE}Testing credentials...{NC}")
186
+
187
+ if user != "Not authenticated" and "Not authenticated" not in user:
188
+ if impersonation_sa != "None":
189
+ # Test user account first by temporarily unsetting impersonation
190
+ orig_impersonation = _get_env_var(
191
+ "CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT"
192
+ )
193
+ if orig_impersonation:
194
+ del os.environ["CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT"]
195
+
196
+ print(f"First with user account {user}:")
197
+ cmd = [gcloud_path, "compute", "zones", "list", "--limit=1"]
198
+ returncode, _, _ = _run_command(cmd, suppress_output=True, check=False)
199
+
200
+ if returncode == 0:
201
+ print(f"{GREEN}✓ User has direct GCP access{NC}")
202
+ else:
203
+ print(f"{YELLOW}✗ User lacks direct GCP access{NC}")
204
+
205
+ # Restore impersonation and test with it
206
+ if orig_impersonation:
207
+ os.environ["CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT"] = (
208
+ orig_impersonation
209
+ )
210
+
211
+ print(f"Then impersonating {impersonation_sa}:")
212
+ returncode, _, _ = _run_command(cmd, suppress_output=True, check=False)
213
+
214
+ if returncode == 0:
215
+ print(f"{GREEN}✓ Successfully using devcon service account{NC}")
216
+ else:
217
+ print(
218
+ f"{RED}Failed to access GCP resources with impersonation. Check permissions.{NC}"
219
+ )
220
+ else:
221
+ # Test user account directly (no impersonation)
222
+ print(f"Using user account {user} (no impersonation):")
223
+ cmd = [gcloud_path, "compute", "zones", "list", "--limit=1"]
224
+ returncode, _, _ = _run_command(cmd, suppress_output=True, check=False)
225
+
226
+ if returncode == 0:
227
+ print(f"{GREEN}✓ Successfully using personal account{NC}")
228
+ else:
229
+ print(f"{RED}Failed to access GCP resources. Check permissions.{NC}")
230
+
231
+
232
+ # --- AWS Functions ---
233
+ def _unset_aws_static_creds() -> None:
234
+ """Unset static AWS credential environment variables."""
235
+ _modify_rc_files("AWS_ACCESS_KEY_ID", None)
236
+ _modify_rc_files("AWS_SECRET_ACCESS_KEY", None)
237
+ _modify_rc_files("AWS_SESSION_TOKEN", None)
238
+
239
+
240
+ def _set_aws_profile(profile: str) -> None:
241
+ """Set and persist AWS profile in environment and RC files."""
242
+ _modify_rc_files("AWS_PROFILE", profile)
243
+ _unset_aws_static_creds()
244
+
245
+
246
+ def _get_current_aws_profile() -> str:
247
+ """Get the current AWS profile."""
248
+ # Check environment variable first
249
+ profile = _get_env_var("AWS_PROFILE")
250
+ if profile:
251
+ return profile
252
+
253
+ # Try using aws command to check
254
+ aws_path = _find_executable("aws")
255
+ try:
256
+ cmd = [aws_path, "configure", "list", "--no-cli-pager"]
257
+ _, stdout, _ = _run_command(cmd, capture=True, check=False)
258
+
259
+ # Extract profile from output
260
+ profile_match = re.search(r"profile\s+(\S+)", stdout)
261
+ if profile_match and profile_match.group(1) not in ("<not", "not"):
262
+ return profile_match.group(1)
263
+ except:
264
+ pass
265
+
266
+ # Default if nothing else works
267
+ return AWS_DEFAULT_PROFILE
268
+
269
+
270
+ def _is_aws_profile_authenticated(profile: str) -> bool:
271
+ """Check if an AWS profile has valid credentials."""
272
+ aws_path = _find_executable("aws")
273
+ cmd = [
274
+ aws_path,
275
+ "sts",
276
+ "get-caller-identity",
277
+ "--profile",
278
+ profile,
279
+ "--no-cli-pager",
280
+ ]
281
+ returncode, _, _ = _run_command(cmd, suppress_output=True, check=False)
282
+ return returncode == 0
283
+
284
+
285
+ def _run_aws_sso_login(profile: str) -> None:
286
+ """Run the AWS SSO login command for a specific profile."""
287
+ aws_path = _find_executable("aws")
288
+ print(f"{BLUE}Running 'aws sso login --profile {profile}'...{NC}")
289
+ _run_command([aws_path, "sso", "login", "--profile", profile])
290
+ print(f"{GREEN}Authentication complete.{NC}")
291
+
292
+
293
+ def _get_available_aws_profiles() -> List[str]:
294
+ """Get list of available AWS profiles from config file."""
295
+ profiles = []
296
+
297
+ if not AWS_CONFIG_FILE.exists():
298
+ return profiles
299
+
300
+ try:
301
+ with open(AWS_CONFIG_FILE, "r") as f:
302
+ lines = f.readlines()
303
+
304
+ for line in lines:
305
+ # Match [profile name] or [name] if default profile
306
+ match = re.match(r"^\[(?:profile\s+)?([^\]]+)\]", line.strip())
307
+ if match:
308
+ profiles.append(match.group(1))
309
+ except:
310
+ pass
311
+
312
+ return profiles
313
+
314
+
315
+ # --- Typer Applications ---
316
+ gcp_app = typer.Typer(help="Manage GCP authentication and impersonation.")
317
+ aws_app = typer.Typer(help="Manage AWS SSO authentication.")
318
+
319
+
320
+ # --- GCP Commands ---
321
+ @gcp_app.command("status")
322
+ def gcp_status():
323
+ """Show current GCP authentication and impersonation status."""
324
+ user_account = _get_current_gcp_user()
325
+ impersonated_sa = _get_current_gcp_impersonation()
326
+
327
+ print(f"{BLUE}GCP Status:{NC}")
328
+ print(f"User account: {GREEN}{user_account}{NC}")
329
+ print(f"Service account: {GREEN}{impersonated_sa}{NC}")
330
+ print(f"Project: {GREEN}{GCP_PROJECT_ID}{NC}")
331
+ print(
332
+ f"Mode: {GREEN}{'Service account impersonation' if impersonated_sa != 'None' else 'Personal account'}{NC}"
333
+ )
334
+
335
+ _test_gcp_credentials(user_account, impersonated_sa)
336
+
337
+
338
+ @gcp_app.command("login")
339
+ def gcp_login():
340
+ """Authenticate with GCP using your Google account."""
341
+ _run_gcloud_login()
342
+ print("\nTo activate devcon service account impersonation, run:")
343
+ print(f' {YELLOW}eval "$(dh gcp use-devcon --export)"{NC}')
344
+ print("To use your personal account permissions, run:")
345
+ print(f' {YELLOW}eval "$(dh gcp use-user --export)"{NC}')
346
+
347
+
348
+ @gcp_app.command("use-devcon")
349
+ def gcp_use_devcon(
350
+ export: bool = typer.Option(
351
+ False, "--export", "-x", help="Print export commands for the current shell."
352
+ ),
353
+ auth_first: bool = typer.Option(
354
+ False, "--auth", "-a", help="Authenticate user first if needed."
355
+ ),
356
+ ):
357
+ """Switch to devcon service account impersonation mode."""
358
+ if not _is_gcp_user_authenticated():
359
+ if auth_first:
360
+ print(
361
+ f"{YELLOW}You need to authenticate first. Running authentication...{NC}",
362
+ file=sys.stderr,
363
+ )
364
+ _run_gcloud_login()
365
+ else:
366
+ print(
367
+ f"{RED}Error: Not authenticated with GCP. Run 'dh gcp login' first or use --auth flag.{NC}",
368
+ file=sys.stderr,
369
+ )
370
+ sys.exit(1)
371
+
372
+ # Modify RC files to persist across sessions
373
+ _modify_rc_files("CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT", f"'{GCP_DEVCON_SA}'")
374
+ _modify_rc_files("GOOGLE_CLOUD_PROJECT", f"'{GCP_PROJECT_ID}'")
375
+
376
+ if export:
377
+ # Print export commands for the current shell to stdout
378
+ print(f"export CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT='{GCP_DEVCON_SA}'")
379
+ print(f"export GOOGLE_CLOUD_PROJECT='{GCP_PROJECT_ID}'")
380
+
381
+ # Print confirmation to stderr so it doesn't affect eval
382
+ print(
383
+ f"{GREEN}GCP service account impersonation for '{GCP_DEVCON_SA}' set up successfully.{NC}",
384
+ file=sys.stderr,
385
+ )
386
+ print(f"{GREEN}You now have standard devcon permissions.{NC}", file=sys.stderr)
387
+ else:
388
+ # Just print confirmation
389
+ print(
390
+ f"{GREEN}Switched to devcon service account impersonation. You now have standard devcon permissions.{NC}"
391
+ )
392
+ print(
393
+ f"Changes will take effect in new shell sessions. To apply in current shell, run:"
394
+ )
395
+ print(f' {YELLOW}eval "$(dh gcp use-devcon --export)"{NC}')
396
+
397
+
398
+ @gcp_app.command("use-user")
399
+ def gcp_use_user(
400
+ export: bool = typer.Option(
401
+ False, "--export", "-x", help="Print export commands for the current shell."
402
+ ),
403
+ auth_first: bool = typer.Option(
404
+ False, "--auth", "-a", help="Authenticate user first if needed."
405
+ ),
406
+ ):
407
+ """Switch to personal account mode (no impersonation)."""
408
+ if not _is_gcp_user_authenticated():
409
+ if auth_first:
410
+ print(
411
+ f"{YELLOW}You need to authenticate first. Running authentication...{NC}",
412
+ file=sys.stderr,
413
+ )
414
+ _run_gcloud_login()
415
+ else:
416
+ print(
417
+ f"{RED}Error: Not authenticated with GCP. Run 'dh gcp login' first or use --auth flag.{NC}",
418
+ file=sys.stderr,
419
+ )
420
+ sys.exit(1)
421
+
422
+ # Modify RC files to persist across sessions
423
+ _modify_rc_files("CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT", None)
424
+ _modify_rc_files("GOOGLE_CLOUD_PROJECT", f"'{GCP_PROJECT_ID}'")
425
+
426
+ if export:
427
+ # Print export commands for the current shell to stdout
428
+ print(f"unset CLOUDSDK_AUTH_IMPERSONATE_SERVICE_ACCOUNT")
429
+ print(f"export GOOGLE_CLOUD_PROJECT='{GCP_PROJECT_ID}'")
430
+
431
+ # Print confirmation to stderr so it doesn't affect eval
432
+ print(
433
+ f"{GREEN}Switched to personal account mode. You are now using your own permissions.{NC}",
434
+ file=sys.stderr,
435
+ )
436
+ else:
437
+ # Just print confirmation
438
+ print(
439
+ f"{GREEN}Switched to personal account mode. You are now using your own permissions.{NC}"
440
+ )
441
+ print(
442
+ f"Changes will take effect in new shell sessions. To apply in current shell, run:"
443
+ )
444
+ print(f' {YELLOW}eval "$(dh gcp use-user --export)"{NC}')
445
+
446
+
447
+ # --- AWS Commands ---
448
+ @aws_app.command("status")
449
+ def aws_status(
450
+ profile: Optional[str] = typer.Option(
451
+ None, "--profile", "-p", help="Check specific profile instead of current."
452
+ )
453
+ ):
454
+ """Show current AWS authentication status."""
455
+ target_profile = profile or _get_current_aws_profile()
456
+ print(f"{BLUE}AWS profile:{NC} {GREEN}{target_profile}{NC}")
457
+
458
+ if _is_aws_profile_authenticated(target_profile):
459
+ print(f"Credential status: {GREEN}valid{NC}")
460
+ # Get detailed identity information
461
+ aws_path = _find_executable("aws")
462
+ _run_command(
463
+ [aws_path, "sts", "get-caller-identity", "--profile", target_profile]
464
+ )
465
+ else:
466
+ print(f"Credential status: {RED}not authenticated{NC}")
467
+ print(f"\nTo authenticate, run:")
468
+ print(f" {YELLOW}dh aws login --profile {target_profile}{NC}")
469
+
470
+
471
+ @aws_app.command("login")
472
+ def aws_login(
473
+ profile: Optional[str] = typer.Option(
474
+ None, "--profile", "-p", help="Login to specific profile instead of current."
475
+ )
476
+ ):
477
+ """Login to AWS SSO with the specified or current profile."""
478
+ target_profile = profile or _get_current_aws_profile()
479
+ _run_aws_sso_login(target_profile)
480
+ print(f"\nTo activate profile {target_profile} in your current shell, run:")
481
+ print(f' {YELLOW}eval "$(dh aws use-profile {target_profile} --export)"{NC}')
482
+
483
+
484
+ @aws_app.command("use-profile")
485
+ def aws_use_profile(
486
+ profile: str = typer.Argument(..., help="AWS profile name to activate."),
487
+ export: bool = typer.Option(
488
+ False, "--export", "-x", help="Print export commands for the current shell."
489
+ ),
490
+ auto_login: bool = typer.Option(
491
+ False, "--auto-login", "-a", help="Run 'aws sso login' if needed."
492
+ ),
493
+ ):
494
+ """Switch to a specific AWS profile."""
495
+ # Modify RC files to persist across sessions
496
+ _set_aws_profile(profile)
497
+
498
+ if auto_login and not _is_aws_profile_authenticated(profile):
499
+ print(
500
+ f"{YELLOW}Profile '{profile}' not authenticated. Running 'aws sso login'...{NC}",
501
+ file=sys.stderr,
502
+ )
503
+ _run_aws_sso_login(profile)
504
+
505
+ if export:
506
+ # Print export commands for the current shell to stdout
507
+ print(f"export AWS_PROFILE='{profile}'")
508
+ print("unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN")
509
+
510
+ # Print confirmation to stderr so it doesn't affect eval
511
+ print(
512
+ f"{GREEN}AWS profile '{profile}' exported successfully.{NC}",
513
+ file=sys.stderr,
514
+ )
515
+ else:
516
+ # Just print confirmation
517
+ print(f"{GREEN}AWS profile set to '{profile}' and persisted to RC files.{NC}")
518
+ print(
519
+ f"Changes will take effect in new shell sessions. To apply in current shell, run:"
520
+ )
521
+ print(f' {YELLOW}eval "$(dh aws use-profile {profile} --export)"{NC}')
522
+
523
+
524
+ @aws_app.command("interactive")
525
+ def aws_interactive():
526
+ """Launch interactive AWS profile management menu."""
527
+ current_profile = _get_current_aws_profile()
528
+
529
+ print(f"{BLUE}AWS SSO helper – current profile: {GREEN}{current_profile}{NC}")
530
+
531
+ while True:
532
+ choice = questionary.select(
533
+ "Choose an option:",
534
+ choices=[
535
+ f"Authenticate current profile ({current_profile})",
536
+ "Switch profile",
537
+ "Show status",
538
+ "Exit",
539
+ ],
540
+ ).ask()
541
+
542
+ if choice == f"Authenticate current profile ({current_profile})":
543
+ _run_aws_sso_login(current_profile)
544
+ print(f"{GREEN}Authentication complete.{NC}")
545
+ print(f"To activate in your current shell, run:")
546
+ print(
547
+ f' {YELLOW}eval "$(dh aws use-profile {current_profile} --export)"{NC}'
548
+ )
549
+
550
+ elif choice == "Switch profile":
551
+ available_profiles = _get_available_aws_profiles()
552
+
553
+ if not available_profiles:
554
+ print(f"{RED}No AWS profiles found. Check your ~/.aws/config file.{NC}")
555
+ continue
556
+
557
+ for i, prof in enumerate(available_profiles, 1):
558
+ print(f"{i}) {prof}")
559
+
560
+ # Get profile selection by number or name
561
+ sel = questionary.text("Select profile number or name:").ask()
562
+
563
+ if sel.isdigit() and 1 <= int(sel) <= len(available_profiles):
564
+ new_profile = available_profiles[int(sel) - 1]
565
+ elif sel in available_profiles:
566
+ new_profile = sel
567
+ else:
568
+ print(f"{RED}Invalid selection{NC}")
569
+ continue
570
+
571
+ _set_aws_profile(new_profile)
572
+ print(f"{GREEN}Switched to profile {new_profile}{NC}")
573
+ print(f"To activate in your current shell, run:")
574
+ print(f' {YELLOW}eval "$(dh aws use-profile {new_profile} --export)"{NC}')
575
+
576
+ # Ask if they want to authenticate now
577
+ if questionary.confirm(
578
+ "Authenticate this profile now?", default=False
579
+ ).ask():
580
+ _run_aws_sso_login(new_profile)
581
+ print(f"{GREEN}Authentication complete.{NC}")
582
+ print(f"To activate in your current shell, run:")
583
+ print(
584
+ f' {YELLOW}eval "$(dh aws use-profile {new_profile} --export)"{NC}'
585
+ )
586
+
587
+ elif choice == "Show status":
588
+ aws_status()
589
+
590
+ elif choice == "Exit":
591
+ print(f"To activate profile {current_profile} in your current shell, run:")
592
+ print(
593
+ f' {YELLOW}eval "$(dh aws use-profile {current_profile} --export)"{NC}'
594
+ )
595
+ break
596
+
597
+ print() # Add newline between iterations
dayhoff_tools/cli/main.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Entry file for the CLI, which aggregates and aliases all commands."""
2
2
 
3
3
  import typer
4
+ from dayhoff_tools.cli.cloud_commands import aws_app, gcp_app
4
5
  from dayhoff_tools.cli.utility_commands import (
5
6
  add_to_warehouse_typer,
6
7
  build_and_upload_wheel,
@@ -21,6 +22,10 @@ app.command("wadd")(add_to_warehouse_typer)
21
22
  app.command("wancestry")(get_ancestry)
22
23
  app.command("wimport")(import_from_warehouse_typer)
23
24
 
25
+ # Cloud commands
26
+ app.add_typer(gcp_app, name="gcp", help="Manage GCP authentication and impersonation.")
27
+ app.add_typer(aws_app, name="aws", help="Manage AWS SSO authentication.")
28
+
24
29
 
25
30
  @app.command("wheel")
26
31
  def build_and_upload_wheel_command(
@@ -218,34 +218,6 @@ def build_and_upload_wheel(bump_part: str = "patch"):
218
218
  new_version = get_current_version_from_toml()
219
219
  print(f"New version: {new_version}")
220
220
 
221
- # Update other pyproject files with the new version
222
- for pyproject_file in [
223
- "pyproject_gcp.toml",
224
- "pyproject_mac.toml",
225
- ]:
226
- try:
227
- with open(pyproject_file, "r") as f:
228
- content = f.read()
229
- # Use the current_version read earlier for replacement
230
- new_content = content.replace(
231
- f'version = "{current_version}"', f'version = "{new_version}"'
232
- )
233
- if new_content == content:
234
- print(
235
- f"Warning: Version string 'version = \"{current_version}\"' not found in {pyproject_file}. No update performed."
236
- )
237
- else:
238
- with open(pyproject_file, "w") as f:
239
- f.write(new_content)
240
- print(
241
- f"Version bumped from {current_version} to {new_version} in {pyproject_file}"
242
- )
243
- except FileNotFoundError:
244
- print(f"Skipping {pyproject_file} - file not found")
245
- except Exception as e:
246
- print(f"Error updating {pyproject_file}: {e}")
247
- return # Stop if update fails
248
-
249
221
  # Disable keyring to avoid issues in containers/CI
250
222
  print("Disabling Poetry keyring...")
251
223
  subprocess.run(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: dayhoff-tools
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Common tools for all the repos at Dayhoff Labs
5
5
  Author: Daniel Martin-Alarcon
6
6
  Author-email: dma@dayhofflabs.com
@@ -2,9 +2,10 @@ dayhoff_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  dayhoff_tools/chemistry/standardizer.py,sha256=uMn7VwHnx02nc404eO6fRuS4rsl4dvSPf2ElfZDXEpY,11188
3
3
  dayhoff_tools/chemistry/utils.py,sha256=jt-7JgF-GeeVC421acX-bobKbLU_X94KNOW24p_P-_M,2257
4
4
  dayhoff_tools/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
- dayhoff_tools/cli/main.py,sha256=9Ku_HP-rE4J5_VSNnbd4-vMXSicOr-pIydij0V6dE3g,3292
5
+ dayhoff_tools/cli/cloud_commands.py,sha256=XTHylqeZ-CLly_wl--7xQq9Ure_wJbzRZjUIzWhHKiI,20811
6
+ dayhoff_tools/cli/main.py,sha256=DJwtU-D-UDzlj_SOZXzw61a2Jg7hvOQLfwBZCcV3eig,3534
6
7
  dayhoff_tools/cli/swarm_commands.py,sha256=5EyKj8yietvT5lfoz8Zx0iQvVaNgc3SJX1z2zQR6o6M,5614
7
- dayhoff_tools/cli/utility_commands.py,sha256=TQsPbim2RBvkx7I6ZfYxtbBJF2m1jZi6X5W0f8NUvVc,10314
8
+ dayhoff_tools/cli/utility_commands.py,sha256=Q6FyQjJ-Zyj1Qw_jkBZm4xrvtX0NF4_v5_nhcdtNdco,9058
8
9
  dayhoff_tools/deployment/base.py,sha256=u-AjbtHnFLoLt33dhYXHIpV-6jcieMEHHGBGN_U9Hm0,15626
9
10
  dayhoff_tools/deployment/deploy_aws.py,sha256=O0gQxHioSU_sNU8T8MD4wSOPvWc--V8eRRZzlRu035I,16446
10
11
  dayhoff_tools/deployment/deploy_gcp.py,sha256=DxBM4sUzwPK9RWLP9bSfr38n1HHl-TVrp4TsbdN8pUA,5795
@@ -24,7 +25,7 @@ dayhoff_tools/sqlite.py,sha256=jV55ikF8VpTfeQqqlHSbY8OgfyfHj8zgHNpZjBLos_E,18672
24
25
  dayhoff_tools/structure.py,sha256=ufN3gAodQxhnt7psK1VTQeu9rKERmo_PhoxIbB4QKMw,27660
25
26
  dayhoff_tools/uniprot.py,sha256=BZYJQF63OtPcBBnQ7_P9gulxzJtqyorgyuDiPeOJqE4,16456
26
27
  dayhoff_tools/warehouse.py,sha256=TqV8nex1AluNaL4JuXH5zuu9P7qmE89lSo6f_oViy6U,14965
27
- dayhoff_tools-1.0.1.dist-info/METADATA,sha256=9aUbXoJt0B-rBJey-y976hmUt0TuwOjZ9zd4iE9Gz7U,3949
28
- dayhoff_tools-1.0.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
29
- dayhoff_tools-1.0.1.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
30
- dayhoff_tools-1.0.1.dist-info/RECORD,,
28
+ dayhoff_tools-1.0.3.dist-info/METADATA,sha256=2RVN1BBfQkoMzU97u5LgG0IXhZOzbLNQPp2LcEhiOA0,3949
29
+ dayhoff_tools-1.0.3.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
30
+ dayhoff_tools-1.0.3.dist-info/entry_points.txt,sha256=iAf4jteNqW3cJm6CO6czLxjW3vxYKsyGLZ8WGmxamSc,49
31
+ dayhoff_tools-1.0.3.dist-info/RECORD,,