superset-showtime 0.5.7__py3-none-any.whl → 0.5.8__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.

Potentially problematic release.


This version of superset-showtime might be problematic. Click here for more details.

showtime/__init__.py CHANGED
@@ -4,7 +4,7 @@
4
4
  Circus tent emoji state tracking for Apache Superset ephemeral environments.
5
5
  """
6
6
 
7
- __version__ = "0.5.7"
7
+ __version__ = "0.5.8"
8
8
  __author__ = "Maxime Beauchemin"
9
9
  __email__ = "maximebeauchemin@gmail.com"
10
10
 
showtime/cli.py CHANGED
@@ -124,7 +124,7 @@ def start(
124
124
  p(f"Current: {pr.current_show.sha}{ip_info} ({pr.current_show.status})")
125
125
  p("Use 'showtime sync' to update or 'showtime stop' to clean up first")
126
126
  return
127
-
127
+
128
128
  # Handle failed environment replacement
129
129
  if pr.current_show and pr.current_show.status == "failed":
130
130
  p(f"🎪 [bold orange]Replacing failed environment for PR #{pr_number}[/bold orange]")
@@ -332,21 +332,27 @@ def list(
332
332
 
333
333
  # Sort by PR number, then by show type (active first, then building, then orphaned)
334
334
  type_priority = {"active": 1, "building": 2, "orphaned": 3}
335
- sorted_envs = sorted(filtered_envs, key=lambda e: (e["pr_number"], type_priority.get(e["show"].get("show_type", "orphaned"), 3)))
336
-
335
+ sorted_envs = sorted(
336
+ filtered_envs,
337
+ key=lambda e: (
338
+ e["pr_number"],
339
+ type_priority.get(e["show"].get("show_type", "orphaned"), 3),
340
+ ),
341
+ )
342
+
337
343
  for env in sorted_envs:
338
344
  show_data = env["show"]
339
345
  pr_number = env["pr_number"]
340
-
346
+
341
347
  # Show type with appropriate styling (using single-width chars for alignment)
342
348
  show_type = show_data.get("show_type", "orphaned")
343
349
  if show_type == "active":
344
350
  type_display = "* active"
345
- elif show_type == "building":
351
+ elif show_type == "building":
346
352
  type_display = "# building"
347
353
  else:
348
354
  type_display = "! orphaned"
349
-
355
+
350
356
  # Make Superset URL clickable and show full URL
351
357
  if show_data["ip"]:
352
358
  full_url = f"http://{show_data['ip']}:8080"
@@ -371,7 +377,7 @@ def list(
371
377
  try:
372
378
  parts = created_display.replace("T", " ").replace("-", ":")
373
379
  created_display = parts[-8:] # Show just HH:MM:SS
374
- except:
380
+ except Exception:
375
381
  pass # Keep original if parsing fails
376
382
 
377
383
  table.add_row(
@@ -497,6 +503,19 @@ def sync(
497
503
  ) -> None:
498
504
  """🎪 Intelligently sync PR to desired state (called by GitHub Actions)"""
499
505
  try:
506
+ # Validate required Git SHA unless using --check-only
507
+ if not check_only:
508
+ from .core.git_validation import (
509
+ get_validation_error_message,
510
+ should_skip_validation,
511
+ validate_required_sha,
512
+ )
513
+
514
+ if not should_skip_validation():
515
+ is_valid, error_msg = validate_required_sha()
516
+ if not is_valid:
517
+ p(get_validation_error_message())
518
+ raise typer.Exit(1)
500
519
  # Use singletons - no interface creation needed
501
520
  pr = PullRequest.from_id(pr_number)
502
521
 
@@ -651,67 +670,69 @@ def aws_cleanup(
651
670
  """🧹 Clean up orphaned AWS resources without GitHub labels"""
652
671
  try:
653
672
  from .core.aws import AWSInterface
654
-
673
+
655
674
  aws = AWSInterface()
656
-
675
+
657
676
  p("🔍 [bold blue]Scanning for orphaned AWS resources...[/bold blue]")
658
-
677
+
659
678
  # 1. Get all GitHub PRs with circus labels
660
679
  github_services = set()
661
680
  try:
662
681
  all_pr_numbers = PullRequest.find_all_with_environments()
663
682
  p(f"📋 Found {len(all_pr_numbers)} PRs with circus labels:")
664
-
683
+
665
684
  for pr_number in all_pr_numbers:
666
685
  pr = PullRequest.from_id(pr_number)
667
- p(f" 🎪 PR #{pr_number}: {len(pr.shows)} shows, {len(pr.circus_labels)} circus labels")
668
-
686
+ p(
687
+ f" 🎪 PR #{pr_number}: {len(pr.shows)} shows, {len(pr.circus_labels)} circus labels"
688
+ )
689
+
669
690
  for show in pr.shows:
670
691
  service_name = show.ecs_service_name
671
692
  github_services.add(service_name)
672
693
  p(f" 📝 Expected service: {service_name}")
673
-
694
+
674
695
  # Show labels for debugging
675
696
  if not pr.shows:
676
697
  p(f" ⚠️ No shows found, labels: {pr.circus_labels[:3]}...") # First 3 labels
677
-
698
+
678
699
  except Exception as e:
679
700
  p(f"⚠️ GitHub scan failed: {e}")
680
701
  github_services = set()
681
-
702
+
682
703
  # 2. Get all AWS ECS services matching showtime pattern
683
704
  p("\n☁️ [bold blue]Scanning AWS ECS services...[/bold blue]")
684
705
  try:
685
706
  aws_services = aws.find_showtime_services()
686
707
  p(f"🔍 Found {len(aws_services)} AWS services with pr-* pattern")
687
-
708
+
688
709
  for service in aws_services:
689
710
  p(f" ☁️ AWS: {service}")
690
711
  except Exception as e:
691
712
  p(f"❌ AWS scan failed: {e}")
692
713
  return
693
-
714
+
694
715
  # 3. Find orphaned services
695
716
  orphaned = [service for service in aws_services if service not in github_services]
696
-
717
+
697
718
  if not orphaned:
698
719
  p("\n✅ [bold green]No orphaned AWS resources found![/bold green]")
699
720
  return
700
-
721
+
701
722
  p(f"\n🚨 [bold red]Found {len(orphaned)} orphaned AWS resources:[/bold red]")
702
723
  for service in orphaned:
703
724
  p(f" 💰 {service} (consuming resources)")
704
-
725
+
705
726
  if dry_run:
706
727
  p(f"\n🎪 [bold yellow]DRY RUN[/bold yellow] - Would delete {len(orphaned)} services")
707
728
  return
708
-
729
+
709
730
  if not force:
710
731
  confirm = typer.confirm(f"Delete {len(orphaned)} orphaned AWS services?")
711
732
  if not confirm:
712
733
  p("🎪 Cancelled")
713
734
  return
714
-
735
+
715
736
  # 4. Delete orphaned resources
716
737
  deleted_count = 0
717
738
  for service in orphaned:
@@ -732,9 +753,9 @@ def aws_cleanup(
732
753
  p(f"❌ Invalid service name format: {service}")
733
754
  except Exception as e:
734
755
  p(f"❌ Error deleting {service}: {e}")
735
-
756
+
736
757
  p(f"\n🎪 ✅ Cleanup complete: deleted {deleted_count}/{len(orphaned)} services")
737
-
758
+
738
759
  except Exception as e:
739
760
  p(f"❌ AWS cleanup failed: {e}")
740
761
 
@@ -0,0 +1,137 @@
1
+ """
2
+ 🎪 Git SHA Validation for Superset Showtime
3
+
4
+ Validates that the current Git repository contains required commit SHA to prevent
5
+ usage with outdated releases.
6
+ """
7
+
8
+ from typing import Optional, Tuple
9
+
10
+ try:
11
+ from git import InvalidGitRepositoryError, Repo
12
+ except ImportError:
13
+ # Fallback if GitPython is not available
14
+ Repo = None
15
+ InvalidGitRepositoryError = Exception
16
+
17
+
18
+ # Hard-coded required SHA - update this when needed
19
+ REQUIRED_SHA = "277f03c2075a74fbafb55531054fb5083debe5cc" # Placeholder SHA for testing
20
+
21
+
22
+ class GitValidationError(Exception):
23
+ """Raised when Git validation fails"""
24
+
25
+ pass
26
+
27
+
28
+ def is_git_repository(path: str = ".") -> bool:
29
+ """
30
+ Check if the current directory (or specified path) is a Git repository.
31
+
32
+ Args:
33
+ path: Path to check (default: current directory)
34
+
35
+ Returns:
36
+ True if it's a Git repository, False otherwise
37
+ """
38
+ if Repo is None:
39
+ # GitPython not available, assume not a git repo
40
+ return False
41
+
42
+ try:
43
+ Repo(path)
44
+ return True
45
+ except (InvalidGitRepositoryError, Exception):
46
+ return False
47
+
48
+
49
+ def validate_required_sha(required_sha: Optional[str] = None) -> Tuple[bool, Optional[str]]:
50
+ """
51
+ Validate that the required SHA exists in the current Git repository.
52
+
53
+ Args:
54
+ required_sha: SHA to validate (default: REQUIRED_SHA constant)
55
+
56
+ Returns:
57
+ Tuple of (is_valid, error_message)
58
+ - (True, None) if validation passes
59
+ - (False, error_message) if validation fails
60
+ """
61
+ if Repo is None:
62
+ return False, "GitPython not available for SHA validation"
63
+
64
+ sha_to_check = required_sha or REQUIRED_SHA
65
+ if not sha_to_check:
66
+ return True, None # No requirement set
67
+
68
+ try:
69
+ repo = Repo(".")
70
+
71
+ # Check if SHA exists in repository
72
+ try:
73
+ commit = repo.commit(sha_to_check)
74
+
75
+ # Check if SHA is reachable from current HEAD
76
+ # This ensures the required commit is in our branch history
77
+ try:
78
+ repo.merge_base(commit, repo.head.commit)
79
+ return True, None
80
+ except Exception:
81
+ # SHA exists but not in current branch history
82
+ return False, (
83
+ f"Required commit {sha_to_check} exists but is not reachable from current HEAD. "
84
+ f"Please ensure your branch includes the required commit."
85
+ )
86
+
87
+ except Exception:
88
+ # SHA doesn't exist in repository
89
+ return False, (
90
+ f"Required commit {sha_to_check} not found in repository. "
91
+ f"Please update to a branch that includes this commit."
92
+ )
93
+
94
+ except InvalidGitRepositoryError:
95
+ return False, "Current directory is not a Git repository"
96
+ except Exception as e:
97
+ return False, f"Git validation error: {e}"
98
+
99
+
100
+ def get_validation_error_message(required_sha: Optional[str] = None) -> str:
101
+ """
102
+ Get a user-friendly error message for SHA validation failure.
103
+
104
+ Args:
105
+ required_sha: SHA that was required (default: REQUIRED_SHA)
106
+
107
+ Returns:
108
+ Formatted error message with resolution steps
109
+ """
110
+ sha_to_check = required_sha or REQUIRED_SHA
111
+
112
+ return f"""
113
+ 🎪 [bold red]Git SHA Validation Failed[/bold red]
114
+
115
+ This branch requires commit {sha_to_check} to be present in your Git history.
116
+
117
+ [bold yellow]To resolve this:[/bold yellow]
118
+ 1. Ensure you're on the correct branch (usually main)
119
+ 2. Pull the latest changes: [cyan]git pull origin main[/cyan]
120
+ 3. Verify the commit exists: [cyan]git log --oneline | grep {sha_to_check[:7]}[/cyan]
121
+ 4. If needed, switch to main branch: [cyan]git checkout main[/cyan]
122
+
123
+ [dim]This check prevents Showtime from running on outdated releases.[/dim]
124
+ """.strip()
125
+
126
+
127
+ def should_skip_validation() -> bool:
128
+ """
129
+ Determine if Git validation should be skipped.
130
+
131
+ Currently skips validation when not in a Git repository,
132
+ allowing --check-only to work in non-Git environments.
133
+
134
+ Returns:
135
+ True if validation should be skipped
136
+ """
137
+ return not is_git_repository()
@@ -43,6 +43,14 @@
43
43
  {
44
44
  "name": "PYTHONUNBUFFERED",
45
45
  "value": "1"
46
+ },
47
+ {
48
+ "name": "SERVER_WORKER_AMOUNT",
49
+ "value": "4"
50
+ },
51
+ {
52
+ "name": "SERVER_THREADS_AMOUNT",
53
+ "value": "1"
46
54
  }
47
55
  ],
48
56
  "mountPoints": [],
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: superset-showtime
3
- Version: 0.5.7
3
+ Version: 0.5.8
4
4
  Summary: 🎪 Apache Superset ephemeral environment management with circus tent emoji state tracking
5
5
  Project-URL: Homepage, https://github.com/apache/superset-showtime
6
6
  Project-URL: Documentation, https://superset-showtime.readthedocs.io/
@@ -1,16 +1,17 @@
1
- showtime/__init__.py,sha256=lAvOPT8KUJ8XkRdEWPjaQO-B9Koo-Mx_42ac173FZv0,448
1
+ showtime/__init__.py,sha256=BG1XZS3HxkBt1uX9xaRqgw4rRho6Q6Yy3aTxcQxcPeA,448
2
2
  showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
3
- showtime/cli.py,sha256=cQB5kH-XWFX3MhsxRaChVdjVe_I_7kBVyFY4fQrHPWA,30542
3
+ showtime/cli.py,sha256=eWu6MBPo0ERc_3rYRGwg_95ZZN8uqlqORBiRAbvbCpg,30933
4
4
  showtime/core/__init__.py,sha256=54hbdFNGrzuNMBdraezfjT8Zi6g221pKlJ9mREnKwCw,34
5
5
  showtime/core/aws.py,sha256=uTjJAvEBQMyTccS93WZeNPhfeKQhJgOQQ0BJdnQjvCU,35007
6
6
  showtime/core/emojis.py,sha256=MHEDuPIdfNiop4zbNLuviz3eY05QiftYSHHCVbkfKhw,2129
7
+ showtime/core/git_validation.py,sha256=h4MZPIaJB1KN6aO5sXf5j4IKOu5__nmZZSDRjWRoEpA,4127
7
8
  showtime/core/github.py,sha256=uETvKDO2Yhpqg3fxLtrKaCuZR3b-1LVmgnf5aLcqrAQ,9988
8
9
  showtime/core/github_messages.py,sha256=MfgwCukrEsWWesMsuL8saciDgP4nS-gijzu8DXr-Alg,7450
9
10
  showtime/core/label_colors.py,sha256=efhbFnz_3nqEnEqmgyF6_hZbxtCu_fmb68BIIUpSsnk,3895
10
11
  showtime/core/pull_request.py,sha256=vKlPPlIeSNT_WmlZkOekLH_tGYm4pyJUZFHQC9dVCKY,27756
11
12
  showtime/core/show.py,sha256=FpxDm52LASCJvf8UF998AtNiVzfdYIwNEsPAsOAAwL0,9701
12
- showtime/data/ecs-task-definition.json,sha256=P7NQljXZTOPW1fWdehGrwJGAh5vdFK9-LzGT59q1PU0,2098
13
- superset_showtime-0.5.7.dist-info/METADATA,sha256=yu5OIoXzL0OZIJf8-9Qrd94zCDXLLI0Om8kNBZWZYoY,12052
14
- superset_showtime-0.5.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
15
- superset_showtime-0.5.7.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
16
- superset_showtime-0.5.7.dist-info/RECORD,,
13
+ showtime/data/ecs-task-definition.json,sha256=Y8ZbAAXdIDMnM-8zzfdUTlwp5RpWSyhe1gtfygKkqW8,2343
14
+ superset_showtime-0.5.8.dist-info/METADATA,sha256=pQD1Wr9BmGSgYwSyr17GwYpsh7QGUyFNKDmioltw08M,12052
15
+ superset_showtime-0.5.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ superset_showtime-0.5.8.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
17
+ superset_showtime-0.5.8.dist-info/RECORD,,