superset-showtime 0.5.6__tar.gz → 0.5.8__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.
Potentially problematic release.
This version of superset-showtime might be problematic. Click here for more details.
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/.claude/settings.local.json +4 -1
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/CLAUDE.md +6 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/PKG-INFO +1 -1
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/__init__.py +1 -1
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/cli.py +46 -25
- superset_showtime-0.5.8/showtime/core/git_validation.py +137 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/pull_request.py +5 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/data/ecs-task-definition.json +8 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/.gitignore +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/.pre-commit-config.yaml +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/Makefile +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/README.md +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/dev-setup.sh +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/pypi-push.sh +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/pyproject.toml +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/requirements-dev.txt +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/requirements.txt +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/__main__.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/__init__.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/aws.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/emojis.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/github.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/github_messages.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/label_colors.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/showtime/core/show.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/__init__.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/unit/__init__.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/unit/test_label_transitions.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/unit/test_pull_request.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/unit/test_sha_specific_logic.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/tests/unit/test_show.py +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/uv.lock +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/workflows-reference/showtime-cleanup.yml +0 -0
- {superset_showtime-0.5.6 → superset_showtime-0.5.8}/workflows-reference/showtime-trigger.yml +0 -0
|
@@ -127,6 +127,12 @@ The `PullRequest.sync()` method implements an atomic claim pattern:
|
|
|
127
127
|
- `GITHUB_ORG` - Default: apache
|
|
128
128
|
- `GITHUB_REPO` - Default: superset
|
|
129
129
|
|
|
130
|
+
**Superset Configuration (via ECS task definition):**
|
|
131
|
+
- `SERVER_WORKER_AMOUNT` - Gunicorn worker processes (default: 1)
|
|
132
|
+
- `SERVER_THREADS_AMOUNT` - Threads per worker (default: 20)
|
|
133
|
+
|
|
134
|
+
See `showtime/data/ecs-task-definition.json` for complete container environment configuration.
|
|
135
|
+
|
|
130
136
|
## GitHub Actions Integration
|
|
131
137
|
|
|
132
138
|
The tool is designed to be called from GitHub Actions workflows:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: superset-showtime
|
|
3
|
-
Version: 0.5.
|
|
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/
|
|
@@ -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(
|
|
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(
|
|
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()
|
|
@@ -599,6 +599,11 @@ class PullRequest:
|
|
|
599
599
|
for old_pointer in existing_active_pointers:
|
|
600
600
|
print(f"🎯 Removing old active pointer: {old_pointer}")
|
|
601
601
|
get_github().remove_label(self.pr_number, old_pointer)
|
|
602
|
+
|
|
603
|
+
# CRITICAL: Refresh after removals before differential calculation
|
|
604
|
+
if existing_active_pointers:
|
|
605
|
+
print("🔄 Refreshing labels after pointer cleanup...")
|
|
606
|
+
self.refresh_labels()
|
|
602
607
|
|
|
603
608
|
# Now do normal differential updates - only for this SHA
|
|
604
609
|
current_sha_labels = {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{superset_showtime-0.5.6 → superset_showtime-0.5.8}/workflows-reference/showtime-cleanup.yml
RENAMED
|
File without changes
|
{superset_showtime-0.5.6 → superset_showtime-0.5.8}/workflows-reference/showtime-trigger.yml
RENAMED
|
File without changes
|