superset-showtime 0.5.0__tar.gz → 0.5.3__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.0 → superset_showtime-0.5.3}/PKG-INFO +1 -1
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/__init__.py +1 -1
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/cli.py +1 -1
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/aws.py +39 -13
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/pull_request.py +80 -4
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/show.py +9 -11
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/.claude/settings.local.json +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/.gitignore +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/.pre-commit-config.yaml +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/CLAUDE.md +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/Makefile +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/README.md +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/dev-setup.sh +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/pypi-push.sh +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/pyproject.toml +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/requirements-dev.txt +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/requirements.txt +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/__main__.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/__init__.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/emojis.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/github.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/github_messages.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/core/label_colors.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/showtime/data/ecs-task-definition.json +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/__init__.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/unit/__init__.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/unit/test_label_transitions.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/unit/test_pull_request.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/unit/test_sha_specific_logic.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/tests/unit/test_show.py +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/uv.lock +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/workflows-reference/showtime-cleanup.yml +0 -0
- {superset_showtime-0.5.0 → superset_showtime-0.5.3}/workflows-reference/showtime-trigger.yml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: superset-showtime
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.3
|
|
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/
|
|
@@ -722,7 +722,7 @@ def aws_cleanup(
|
|
|
722
722
|
parts = pr_match.split("-")
|
|
723
723
|
if len(parts) >= 2:
|
|
724
724
|
pr_number = int(parts[0])
|
|
725
|
-
success = aws.delete_environment(service
|
|
725
|
+
success = aws.delete_environment(service, pr_number)
|
|
726
726
|
if success:
|
|
727
727
|
p(f"✅ Deleted {service}")
|
|
728
728
|
deleted_count += 1
|
|
@@ -175,36 +175,62 @@ class AWSInterface:
|
|
|
175
175
|
|
|
176
176
|
def delete_environment(self, service_name: str, pr_number: int) -> bool:
|
|
177
177
|
"""
|
|
178
|
-
Delete ephemeral environment
|
|
178
|
+
Delete ephemeral environment with proper verification
|
|
179
179
|
|
|
180
180
|
Steps:
|
|
181
|
-
1. Check if ECS service exists
|
|
182
|
-
2. Delete ECS service with
|
|
181
|
+
1. Check if ECS service exists
|
|
182
|
+
2. Delete ECS service with force and wait for completion
|
|
183
183
|
3. Delete ECR image tag
|
|
184
|
+
4. Verify deletion completed
|
|
184
185
|
"""
|
|
185
186
|
try:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
187
|
+
ecs_service_name = f"{service_name}-service" if not service_name.endswith("-service") else service_name
|
|
188
|
+
print(f"🗑️ Deleting ECS service: {ecs_service_name}")
|
|
189
|
+
|
|
190
|
+
# Step 1: Check if service exists
|
|
191
|
+
if not self._service_exists(ecs_service_name):
|
|
192
|
+
print(f"✅ Service {ecs_service_name} already deleted")
|
|
193
|
+
return True
|
|
194
|
+
|
|
195
|
+
# Step 2: Delete ECS service (force delete) and wait
|
|
196
|
+
print(f"☁️ Initiating ECS service deletion...")
|
|
197
|
+
delete_response = self.ecs_client.delete_service(
|
|
198
|
+
cluster=self.cluster,
|
|
199
|
+
service=ecs_service_name,
|
|
200
|
+
force=True
|
|
201
|
+
)
|
|
202
|
+
print(f"🔄 Delete initiated: {delete_response.get('service', {}).get('status', 'unknown')}")
|
|
189
203
|
|
|
190
|
-
# Step
|
|
191
|
-
|
|
204
|
+
# Step 3: Wait for deletion to complete (crucial!)
|
|
205
|
+
print(f"⏳ Waiting for service deletion to complete...")
|
|
206
|
+
deletion_success = self._wait_for_service_deletion(ecs_service_name, timeout_minutes=10)
|
|
207
|
+
|
|
208
|
+
if not deletion_success:
|
|
209
|
+
print(f"⚠️ Service deletion timeout - service may still exist")
|
|
210
|
+
return False
|
|
192
211
|
|
|
193
|
-
# Step
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
212
|
+
# Step 4: Delete ECR image tag
|
|
213
|
+
print(f"🐳 Cleaning up Docker image...")
|
|
214
|
+
# Fix SHA extraction: pr-34831-ac533ec-service → ac533ec
|
|
215
|
+
# Remove "pr-" prefix and "-service" suffix, then get SHA (last part)
|
|
216
|
+
base_name = service_name.replace("pr-", "").replace("-service", "")
|
|
217
|
+
parts = base_name.split("-")
|
|
218
|
+
sha = parts[-1] if len(parts) > 1 else base_name # Last part is SHA
|
|
219
|
+
image_tag = f"pr-{pr_number}-{sha}-ci"
|
|
197
220
|
|
|
198
221
|
try:
|
|
199
222
|
self.ecr_client.batch_delete_image(
|
|
200
223
|
repositoryName=self.repository, imageIds=[{"imageTag": image_tag}]
|
|
201
224
|
)
|
|
225
|
+
print(f"✅ Deleted ECR image: {image_tag}")
|
|
202
226
|
except self.ecr_client.exceptions.ImageNotFoundException:
|
|
203
|
-
|
|
227
|
+
print(f"ℹ️ ECR image {image_tag} already deleted")
|
|
204
228
|
|
|
229
|
+
print(f"✅ Environment {service_name} fully deleted")
|
|
205
230
|
return True
|
|
206
231
|
|
|
207
232
|
except Exception as e:
|
|
233
|
+
print(f"❌ AWS deletion failed: {e}")
|
|
208
234
|
raise AWSError(
|
|
209
235
|
message=str(e), operation="delete_environment", resource=service_name
|
|
210
236
|
) from e
|
|
@@ -246,6 +246,9 @@ class PullRequest:
|
|
|
246
246
|
print(f"✅ Deployment completed - environment running at {show.ip}:8080")
|
|
247
247
|
self._update_show_labels(show, dry_run_github)
|
|
248
248
|
|
|
249
|
+
# Blue-green cleanup: stop all other environments for this PR
|
|
250
|
+
cleaned_count = self.stop_previous_environments(show.sha, dry_run_github, dry_run_aws)
|
|
251
|
+
|
|
249
252
|
# Show AWS console URLs for monitoring
|
|
250
253
|
self._show_service_urls(show)
|
|
251
254
|
|
|
@@ -278,6 +281,9 @@ class PullRequest:
|
|
|
278
281
|
print(f"✅ Rolling update completed - new environment at {new_show.ip}:8080")
|
|
279
282
|
self._update_show_labels(new_show, dry_run_github)
|
|
280
283
|
|
|
284
|
+
# Blue-green cleanup: stop all other environments for this PR
|
|
285
|
+
cleaned_count = self.stop_previous_environments(new_show.sha, dry_run_github, dry_run_aws)
|
|
286
|
+
|
|
281
287
|
# Show AWS console URLs for monitoring
|
|
282
288
|
self._show_service_urls(new_show)
|
|
283
289
|
|
|
@@ -450,17 +456,31 @@ class PullRequest:
|
|
|
450
456
|
|
|
451
457
|
# 2. Remove trigger labels (atomic operation)
|
|
452
458
|
trigger_labels = [label for label in self.labels if "showtime-trigger-" in label]
|
|
453
|
-
|
|
454
|
-
|
|
459
|
+
if trigger_labels:
|
|
460
|
+
print(f"🏷️ Removing trigger labels: {trigger_labels}")
|
|
461
|
+
for trigger_label in trigger_labels:
|
|
462
|
+
get_github().remove_label(self.pr_number, trigger_label)
|
|
463
|
+
else:
|
|
464
|
+
print("🏷️ No trigger labels to remove")
|
|
455
465
|
|
|
456
466
|
# 3. Set building state immediately (claim the PR)
|
|
457
467
|
if action in ["create_environment", "rolling_update", "auto_sync"]:
|
|
458
468
|
building_show = self._create_new_show(target_sha)
|
|
459
469
|
building_show.status = "building"
|
|
470
|
+
|
|
460
471
|
# Update labels to reflect building state
|
|
472
|
+
print(f"🏷️ Removing existing circus labels...")
|
|
461
473
|
get_github().remove_circus_labels(self.pr_number)
|
|
462
|
-
|
|
463
|
-
|
|
474
|
+
|
|
475
|
+
new_labels = building_show.to_circus_labels()
|
|
476
|
+
print(f"🏷️ Creating new labels: {new_labels}")
|
|
477
|
+
for label in new_labels:
|
|
478
|
+
try:
|
|
479
|
+
get_github().add_label(self.pr_number, label)
|
|
480
|
+
print(f" ✅ Added: {label}")
|
|
481
|
+
except Exception as e:
|
|
482
|
+
print(f" ❌ Failed to add {label}: {e}")
|
|
483
|
+
raise
|
|
464
484
|
|
|
465
485
|
return True
|
|
466
486
|
|
|
@@ -566,6 +586,17 @@ class PullRequest:
|
|
|
566
586
|
for old_status_label in sha_status_labels:
|
|
567
587
|
get_github().remove_label(self.pr_number, old_status_label)
|
|
568
588
|
|
|
589
|
+
# For running environments, ensure only ONE active pointer exists
|
|
590
|
+
if show.status == "running":
|
|
591
|
+
# Remove ALL existing active pointers (there should only be one)
|
|
592
|
+
existing_active_pointers = [
|
|
593
|
+
label for label in self.labels
|
|
594
|
+
if label.startswith("🎪 🎯 ")
|
|
595
|
+
]
|
|
596
|
+
for old_pointer in existing_active_pointers:
|
|
597
|
+
print(f"🎯 Removing old active pointer: {old_pointer}")
|
|
598
|
+
get_github().remove_label(self.pr_number, old_pointer)
|
|
599
|
+
|
|
569
600
|
# Now do normal differential updates - only for this SHA
|
|
570
601
|
current_sha_labels = {
|
|
571
602
|
label for label in self.labels
|
|
@@ -602,3 +633,48 @@ class PullRequest:
|
|
|
602
633
|
print(f"📝 Logs: {urls['logs']}")
|
|
603
634
|
print(f"📊 Service: {urls['service']}")
|
|
604
635
|
print("")
|
|
636
|
+
|
|
637
|
+
def stop_previous_environments(self, keep_sha: str, dry_run_github: bool = False, dry_run_aws: bool = False) -> int:
|
|
638
|
+
"""Stop all environments except the specified SHA (blue-green cleanup)
|
|
639
|
+
|
|
640
|
+
Args:
|
|
641
|
+
keep_sha: SHA of environment to keep running
|
|
642
|
+
dry_run_github: Skip GitHub label operations
|
|
643
|
+
dry_run_aws: Skip AWS operations
|
|
644
|
+
|
|
645
|
+
Returns:
|
|
646
|
+
Number of environments stopped
|
|
647
|
+
"""
|
|
648
|
+
stopped_count = 0
|
|
649
|
+
|
|
650
|
+
for show in self.shows:
|
|
651
|
+
if show.sha != keep_sha:
|
|
652
|
+
print(f"🧹 Cleaning up old environment: {show.sha} ({show.status})")
|
|
653
|
+
try:
|
|
654
|
+
show.stop(dry_run_github=dry_run_github, dry_run_aws=dry_run_aws)
|
|
655
|
+
|
|
656
|
+
# Remove labels for this old environment
|
|
657
|
+
if not dry_run_github:
|
|
658
|
+
old_labels = show.to_circus_labels()
|
|
659
|
+
print(f"🏷️ Removing labels for {show.sha}: {len(old_labels)} labels")
|
|
660
|
+
for label in old_labels:
|
|
661
|
+
try:
|
|
662
|
+
get_github().remove_label(self.pr_number, label)
|
|
663
|
+
except Exception as e:
|
|
664
|
+
print(f"⚠️ Failed to remove label {label}: {e}")
|
|
665
|
+
|
|
666
|
+
stopped_count += 1
|
|
667
|
+
print(f"✅ Stopped environment {show.sha}")
|
|
668
|
+
|
|
669
|
+
except Exception as e:
|
|
670
|
+
print(f"❌ Failed to stop environment {show.sha}: {e}")
|
|
671
|
+
|
|
672
|
+
if stopped_count > 0:
|
|
673
|
+
print(f"🧹 Blue-green cleanup: stopped {stopped_count} old environments")
|
|
674
|
+
# Refresh labels after cleanup
|
|
675
|
+
if not dry_run_github:
|
|
676
|
+
self.refresh_labels()
|
|
677
|
+
else:
|
|
678
|
+
print("ℹ️ No old environments to clean up")
|
|
679
|
+
|
|
680
|
+
return stopped_count
|
|
@@ -165,17 +165,17 @@ class Show:
|
|
|
165
165
|
|
|
166
166
|
# Detect if running in CI environment
|
|
167
167
|
is_ci = bool(os.getenv("GITHUB_ACTIONS") or os.getenv("CI"))
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
# Build command without final path
|
|
170
170
|
cmd = [
|
|
171
171
|
"docker",
|
|
172
|
-
"buildx",
|
|
172
|
+
"buildx",
|
|
173
173
|
"build",
|
|
174
174
|
"--push",
|
|
175
175
|
"--platform",
|
|
176
176
|
"linux/amd64",
|
|
177
177
|
"--target",
|
|
178
|
-
"
|
|
178
|
+
"showtime",
|
|
179
179
|
"--build-arg",
|
|
180
180
|
"INCLUDE_CHROMIUM=false",
|
|
181
181
|
"--build-arg",
|
|
@@ -190,7 +190,7 @@ class Show:
|
|
|
190
190
|
cmd.extend([
|
|
191
191
|
"--cache-from",
|
|
192
192
|
"type=registry,ref=apache/superset-cache:showtime",
|
|
193
|
-
"--cache-to",
|
|
193
|
+
"--cache-to",
|
|
194
194
|
"type=registry,mode=max,ref=apache/superset-cache:showtime",
|
|
195
195
|
])
|
|
196
196
|
print("🐳 CI environment: Using full registry caching")
|
|
@@ -202,16 +202,14 @@ class Show:
|
|
|
202
202
|
])
|
|
203
203
|
print("🐳 Local environment: Using cache-from only (no export)")
|
|
204
204
|
|
|
205
|
-
# Add --load only when
|
|
206
|
-
# Intel Mac/Linux can load linux/amd64, Apple Silicon cannot
|
|
207
|
-
native_x86 = platform.machine() in ("x86_64", "AMD64")
|
|
205
|
+
# Add --load only when explicitly requested for local testing
|
|
208
206
|
force_load = os.getenv("DOCKER_LOAD", "false").lower() == "true"
|
|
209
|
-
|
|
210
|
-
if
|
|
207
|
+
|
|
208
|
+
if force_load:
|
|
211
209
|
cmd.append("--load")
|
|
212
|
-
print("🐳 Will load image to local Docker daemon (
|
|
210
|
+
print("🐳 Will load image to local Docker daemon (DOCKER_LOAD=true)")
|
|
213
211
|
else:
|
|
214
|
-
print("🐳
|
|
212
|
+
print("🐳 Push-only build (no local load) - faster for CI/deployment")
|
|
215
213
|
|
|
216
214
|
# Add build context path last
|
|
217
215
|
cmd.append(".")
|
|
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
|
|
File without changes
|
{superset_showtime-0.5.0 → superset_showtime-0.5.3}/workflows-reference/showtime-cleanup.yml
RENAMED
|
File without changes
|
{superset_showtime-0.5.0 → superset_showtime-0.5.3}/workflows-reference/showtime-trigger.yml
RENAMED
|
File without changes
|