superset-showtime 0.1.0__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 +21 -0
- showtime/__main__.py +8 -0
- showtime/cli.py +1361 -0
- showtime/commands/__init__.py +1 -0
- showtime/commands/start.py +40 -0
- showtime/core/__init__.py +1 -0
- showtime/core/aws.py +758 -0
- showtime/core/circus.py +285 -0
- showtime/core/config.py +152 -0
- showtime/core/emojis.py +86 -0
- showtime/core/github.py +214 -0
- showtime/data/ecs-task-definition.json +59 -0
- superset_showtime-0.1.0.dist-info/METADATA +391 -0
- superset_showtime-0.1.0.dist-info/RECORD +16 -0
- superset_showtime-0.1.0.dist-info/WHEEL +4 -0
- superset_showtime-0.1.0.dist-info/entry_points.txt +3 -0
showtime/core/github.py
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
๐ช GitHub API interface for circus tent label management
|
|
3
|
+
|
|
4
|
+
Handles all GitHub operations including PR fetching, label management,
|
|
5
|
+
and circus tent emoji state synchronization.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class GitHubError(Exception):
|
|
17
|
+
"""GitHub API error"""
|
|
18
|
+
|
|
19
|
+
message: str
|
|
20
|
+
status_code: Optional[int] = None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class GitHubInterface:
|
|
24
|
+
"""GitHub API client for circus tent label operations"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, token: str = None, org: str = None, repo: str = None):
|
|
27
|
+
self.token = token or self._detect_token()
|
|
28
|
+
self.org = org or os.getenv("GITHUB_ORG", "apache")
|
|
29
|
+
self.repo = repo or os.getenv("GITHUB_REPO", "superset")
|
|
30
|
+
self.base_url = "https://api.github.com"
|
|
31
|
+
|
|
32
|
+
if not self.token:
|
|
33
|
+
raise GitHubError("GitHub token required. Set GITHUB_TOKEN environment variable.")
|
|
34
|
+
|
|
35
|
+
def _detect_token(self) -> Optional[str]:
|
|
36
|
+
"""Detect GitHub token from environment or gh CLI"""
|
|
37
|
+
# 1. Environment variable (GHA style)
|
|
38
|
+
token = os.getenv("GITHUB_TOKEN")
|
|
39
|
+
if token:
|
|
40
|
+
return token
|
|
41
|
+
|
|
42
|
+
# 2. GitHub CLI (local development)
|
|
43
|
+
try:
|
|
44
|
+
import subprocess
|
|
45
|
+
|
|
46
|
+
result = subprocess.run(["gh", "auth", "token"], capture_output=True, text=True)
|
|
47
|
+
if result.returncode == 0:
|
|
48
|
+
return result.stdout.strip()
|
|
49
|
+
except FileNotFoundError:
|
|
50
|
+
pass # gh CLI not installed
|
|
51
|
+
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def headers(self) -> Dict[str, str]:
|
|
56
|
+
"""HTTP headers for GitHub API requests"""
|
|
57
|
+
return {
|
|
58
|
+
"Authorization": f"Bearer {self.token}",
|
|
59
|
+
"Accept": "application/vnd.github.v3+json",
|
|
60
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def get_labels(self, pr_number: int) -> List[str]:
|
|
64
|
+
"""Get all labels for a PR"""
|
|
65
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/issues/{pr_number}/labels"
|
|
66
|
+
|
|
67
|
+
with httpx.Client() as client:
|
|
68
|
+
response = client.get(url, headers=self.headers)
|
|
69
|
+
response.raise_for_status()
|
|
70
|
+
|
|
71
|
+
labels_data = response.json()
|
|
72
|
+
return [label["name"] for label in labels_data]
|
|
73
|
+
|
|
74
|
+
def add_label(self, pr_number: int, label: str) -> None:
|
|
75
|
+
"""Add a label to a PR"""
|
|
76
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/issues/{pr_number}/labels"
|
|
77
|
+
|
|
78
|
+
with httpx.Client() as client:
|
|
79
|
+
response = client.post(url, headers=self.headers, json={"labels": [label]})
|
|
80
|
+
response.raise_for_status()
|
|
81
|
+
|
|
82
|
+
def remove_label(self, pr_number: int, label: str) -> None:
|
|
83
|
+
"""Remove a label from a PR"""
|
|
84
|
+
# URL encode the label name for special characters like emojis
|
|
85
|
+
import urllib.parse
|
|
86
|
+
|
|
87
|
+
encoded_label = urllib.parse.quote(label, safe="")
|
|
88
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/issues/{pr_number}/labels/{encoded_label}"
|
|
89
|
+
|
|
90
|
+
with httpx.Client() as client:
|
|
91
|
+
response = client.delete(url, headers=self.headers)
|
|
92
|
+
# 404 is OK - label might not exist
|
|
93
|
+
if response.status_code not in (200, 204, 404):
|
|
94
|
+
response.raise_for_status()
|
|
95
|
+
|
|
96
|
+
def set_labels(self, pr_number: int, labels: List[str]) -> None:
|
|
97
|
+
"""Replace all labels on a PR"""
|
|
98
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/issues/{pr_number}/labels"
|
|
99
|
+
|
|
100
|
+
with httpx.Client() as client:
|
|
101
|
+
response = client.put(url, headers=self.headers, json={"labels": labels})
|
|
102
|
+
response.raise_for_status()
|
|
103
|
+
|
|
104
|
+
def get_latest_commit_sha(self, pr_number: int) -> str:
|
|
105
|
+
"""Get the latest commit SHA for a PR"""
|
|
106
|
+
pr_data = self.get_pr_data(pr_number)
|
|
107
|
+
return pr_data["head"]["sha"]
|
|
108
|
+
|
|
109
|
+
def get_pr_data(self, pr_number: int) -> dict:
|
|
110
|
+
"""Get full PR data including description"""
|
|
111
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/pulls/{pr_number}"
|
|
112
|
+
|
|
113
|
+
with httpx.Client() as client:
|
|
114
|
+
response = client.get(url, headers=self.headers)
|
|
115
|
+
response.raise_for_status()
|
|
116
|
+
return response.json()
|
|
117
|
+
|
|
118
|
+
def get_circus_labels(self, pr_number: int) -> List[str]:
|
|
119
|
+
"""Get only circus tent emoji labels for a PR"""
|
|
120
|
+
all_labels = self.get_labels(pr_number)
|
|
121
|
+
return [label for label in all_labels if label.startswith("๐ช ")]
|
|
122
|
+
|
|
123
|
+
def remove_circus_labels(self, pr_number: int) -> None:
|
|
124
|
+
"""Remove all circus tent labels from a PR"""
|
|
125
|
+
circus_labels = self.get_circus_labels(pr_number)
|
|
126
|
+
for label in circus_labels:
|
|
127
|
+
self.remove_label(pr_number, label)
|
|
128
|
+
|
|
129
|
+
def find_prs_with_shows(self) -> List[int]:
|
|
130
|
+
"""Find all PRs that have circus tent labels"""
|
|
131
|
+
# Search for issues with circus tent labels (updated for SHA-first format)
|
|
132
|
+
url = f"{self.base_url}/search/issues"
|
|
133
|
+
# Search for PRs with any circus tent labels
|
|
134
|
+
params = {
|
|
135
|
+
"q": f"repo:{self.org}/{self.repo} is:pr ๐ช",
|
|
136
|
+
"per_page": 100,
|
|
137
|
+
} # Include closed PRs
|
|
138
|
+
|
|
139
|
+
with httpx.Client() as client:
|
|
140
|
+
response = client.get(url, headers=self.headers, params=params)
|
|
141
|
+
response.raise_for_status()
|
|
142
|
+
|
|
143
|
+
issues = response.json()["items"]
|
|
144
|
+
return [issue["number"] for issue in issues]
|
|
145
|
+
|
|
146
|
+
def post_comment(self, pr_number: int, body: str) -> None:
|
|
147
|
+
"""Post a comment on a PR"""
|
|
148
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/issues/{pr_number}/comments"
|
|
149
|
+
|
|
150
|
+
with httpx.Client() as client:
|
|
151
|
+
response = client.post(url, headers=self.headers, json={"body": body})
|
|
152
|
+
response.raise_for_status()
|
|
153
|
+
|
|
154
|
+
def validate_connection(self) -> bool:
|
|
155
|
+
"""Test GitHub API connection"""
|
|
156
|
+
try:
|
|
157
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}"
|
|
158
|
+
with httpx.Client() as client:
|
|
159
|
+
response = client.get(url, headers=self.headers)
|
|
160
|
+
response.raise_for_status()
|
|
161
|
+
return True
|
|
162
|
+
except Exception:
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
def get_repository_labels(self) -> List[str]:
|
|
166
|
+
"""Get all labels defined in the repository"""
|
|
167
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/labels"
|
|
168
|
+
|
|
169
|
+
with httpx.Client() as client:
|
|
170
|
+
response = client.get(url, headers=self.headers, params={"per_page": 100})
|
|
171
|
+
response.raise_for_status()
|
|
172
|
+
|
|
173
|
+
labels_data = response.json()
|
|
174
|
+
return [label["name"] for label in labels_data]
|
|
175
|
+
|
|
176
|
+
def delete_repository_label(self, label_name: str) -> bool:
|
|
177
|
+
"""Delete a label definition from the repository"""
|
|
178
|
+
import urllib.parse
|
|
179
|
+
|
|
180
|
+
encoded_label = urllib.parse.quote(label_name, safe="")
|
|
181
|
+
url = f"{self.base_url}/repos/{self.org}/{self.repo}/labels/{encoded_label}"
|
|
182
|
+
|
|
183
|
+
with httpx.Client() as client:
|
|
184
|
+
response = client.delete(url, headers=self.headers)
|
|
185
|
+
# 404 is OK - label might not exist
|
|
186
|
+
if response.status_code in (200, 204):
|
|
187
|
+
return True
|
|
188
|
+
elif response.status_code == 404:
|
|
189
|
+
return False # Label doesn't exist
|
|
190
|
+
else:
|
|
191
|
+
response.raise_for_status()
|
|
192
|
+
|
|
193
|
+
def cleanup_sha_labels(self, dry_run: bool = False) -> List[str]:
|
|
194
|
+
"""Clean up all circus tent labels with SHA patterns from repository"""
|
|
195
|
+
import re
|
|
196
|
+
|
|
197
|
+
all_labels = self.get_repository_labels()
|
|
198
|
+
sha_labels = []
|
|
199
|
+
|
|
200
|
+
# Find labels with SHA patterns (7+ hex chars after ๐ช)
|
|
201
|
+
sha_pattern = re.compile(r"^๐ช .* [a-f0-9]{7,}( .*)?$")
|
|
202
|
+
|
|
203
|
+
for label in all_labels:
|
|
204
|
+
if sha_pattern.match(label):
|
|
205
|
+
sha_labels.append(label)
|
|
206
|
+
|
|
207
|
+
if not dry_run:
|
|
208
|
+
deleted_labels = []
|
|
209
|
+
for label in sha_labels:
|
|
210
|
+
if self.delete_repository_label(label):
|
|
211
|
+
deleted_labels.append(label)
|
|
212
|
+
return deleted_labels
|
|
213
|
+
|
|
214
|
+
return sha_labels
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"containerDefinitions": [
|
|
3
|
+
{
|
|
4
|
+
"name": "superset-ci",
|
|
5
|
+
"image": "apache/superset:latest",
|
|
6
|
+
"cpu": 0,
|
|
7
|
+
"links": [],
|
|
8
|
+
"portMappings": [
|
|
9
|
+
{
|
|
10
|
+
"containerPort": 8080,
|
|
11
|
+
"hostPort": 8080,
|
|
12
|
+
"protocol": "tcp"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"essential": true,
|
|
16
|
+
"entryPoint": [],
|
|
17
|
+
"command": [],
|
|
18
|
+
"environment": [
|
|
19
|
+
{
|
|
20
|
+
"name": "SUPERSET_LOAD_EXAMPLES",
|
|
21
|
+
"value": "yes"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"name": "SUPERSET_PORT",
|
|
25
|
+
"value": "8080"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "SUPERSET_SECRET_KEY",
|
|
29
|
+
"value": "super-secret-for-ephemerals"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "TALISMAN_ENABLED",
|
|
33
|
+
"value": "False"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"mountPoints": [],
|
|
37
|
+
"volumesFrom": [],
|
|
38
|
+
"logConfiguration": {
|
|
39
|
+
"logDriver": "awslogs",
|
|
40
|
+
"options": {
|
|
41
|
+
"awslogs-group": "/ecs/superset-ci",
|
|
42
|
+
"awslogs-region": "us-west-2",
|
|
43
|
+
"awslogs-stream-prefix": "ecs"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
"family": "superset-ci",
|
|
49
|
+
"taskRoleArn": "ecsTaskExecutionRole",
|
|
50
|
+
"executionRoleArn": "ecsTaskExecutionRole",
|
|
51
|
+
"networkMode": "awsvpc",
|
|
52
|
+
"volumes": [],
|
|
53
|
+
"placementConstraints": [],
|
|
54
|
+
"requiresCompatibilities": [
|
|
55
|
+
"FARGATE"
|
|
56
|
+
],
|
|
57
|
+
"cpu": "512",
|
|
58
|
+
"memory": "1024"
|
|
59
|
+
}
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: superset-showtime
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ๐ช Apache Superset ephemeral environment management with circus tent emoji state tracking
|
|
5
|
+
Project-URL: Homepage, https://github.com/apache/superset-showtime
|
|
6
|
+
Project-URL: Documentation, https://superset-showtime.readthedocs.io/
|
|
7
|
+
Project-URL: Repository, https://github.com/apache/superset-showtime.git
|
|
8
|
+
Project-URL: Bug Tracker, https://github.com/apache/superset-showtime/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/apache/superset-showtime/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Maxime Beauchemin <maximebeauchemin@gmail.com>
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
Keywords: aws,circus,devops,environments,ephemeral,github,showtime,superset
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
26
|
+
Classifier: Topic :: System :: Systems Administration
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Requires-Dist: boto3>=1.26.0
|
|
29
|
+
Requires-Dist: botocore>=1.29.0
|
|
30
|
+
Requires-Dist: gitpython>=3.1.0
|
|
31
|
+
Requires-Dist: httpx>=0.24.0
|
|
32
|
+
Requires-Dist: pydantic>=2.0.0
|
|
33
|
+
Requires-Dist: python-dateutil>=2.8.0
|
|
34
|
+
Requires-Dist: pyyaml>=6.0
|
|
35
|
+
Requires-Dist: rich>=10.0.0
|
|
36
|
+
Requires-Dist: typer>=0.9.0
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: azure-mgmt-containerinstance>=10.0.0; extra == 'all'
|
|
39
|
+
Requires-Dist: azure-storage-blob>=12.0.0; extra == 'all'
|
|
40
|
+
Requires-Dist: boto3>=1.26.0; extra == 'all'
|
|
41
|
+
Requires-Dist: botocore>=1.29.0; extra == 'all'
|
|
42
|
+
Requires-Dist: google-cloud-compute>=1.11.0; extra == 'all'
|
|
43
|
+
Requires-Dist: google-cloud-storage>=2.8.0; extra == 'all'
|
|
44
|
+
Requires-Dist: kubernetes>=26.0.0; extra == 'all'
|
|
45
|
+
Provides-Extra: aws
|
|
46
|
+
Requires-Dist: boto3>=1.26.0; extra == 'aws'
|
|
47
|
+
Requires-Dist: botocore>=1.29.0; extra == 'aws'
|
|
48
|
+
Provides-Extra: azure
|
|
49
|
+
Requires-Dist: azure-mgmt-containerinstance>=10.0.0; extra == 'azure'
|
|
50
|
+
Requires-Dist: azure-storage-blob>=12.0.0; extra == 'azure'
|
|
51
|
+
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: build>=1.0.0; extra == 'dev'
|
|
53
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
54
|
+
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
55
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
56
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
57
|
+
Requires-Dist: ruff>=0.1.9; extra == 'dev'
|
|
58
|
+
Requires-Dist: twine>=4.0.0; extra == 'dev'
|
|
59
|
+
Provides-Extra: gcp
|
|
60
|
+
Requires-Dist: google-cloud-compute>=1.11.0; extra == 'gcp'
|
|
61
|
+
Requires-Dist: google-cloud-storage>=2.8.0; extra == 'gcp'
|
|
62
|
+
Provides-Extra: k8s
|
|
63
|
+
Requires-Dist: kubernetes>=26.0.0; extra == 'k8s'
|
|
64
|
+
Description-Content-Type: text/markdown
|
|
65
|
+
|
|
66
|
+
# ๐ช Superset Showtime
|
|
67
|
+
|
|
68
|
+
**Modern ephemeral environment management for Apache Superset using circus tent emoji labels**
|
|
69
|
+
|
|
70
|
+
[](https://badge.fury.io/py/superset-showtime)
|
|
71
|
+
[](https://www.python.org/downloads/)
|
|
72
|
+
|
|
73
|
+
## ๐ฏ What is Showtime?
|
|
74
|
+
|
|
75
|
+
Superset Showtime replaces the complex GitHub Actions scripts for ephemeral environments with a simple, powerful CLI tool that uses **circus tent emoji labels** for state management.
|
|
76
|
+
|
|
77
|
+
### The Problem We Solve
|
|
78
|
+
|
|
79
|
+
**Current Superset ephemeral environment issues:**
|
|
80
|
+
- ๐จ **Stale environments** - New commits don't update existing environments
|
|
81
|
+
- ๐ธ **Resource waste** - Multiple environments per PR, no automatic cleanup
|
|
82
|
+
- ๐ง **Hard to maintain** - Complex GitHub Actions logic scattered across workflows
|
|
83
|
+
- ๐ **Poor visibility** - Hard to see what environments exist and their status
|
|
84
|
+
|
|
85
|
+
### The Showtime Solution
|
|
86
|
+
|
|
87
|
+
**๐ช GitHub labels become a visual state machine:**
|
|
88
|
+
```bash
|
|
89
|
+
# User adds trigger label in GitHub UI:
|
|
90
|
+
๐ช trigger-start
|
|
91
|
+
|
|
92
|
+
# System responds with state labels:
|
|
93
|
+
๐ช abc123f ๐ฆ building # Environment abc123f is building
|
|
94
|
+
๐ช ๐ฏ abc123f # abc123f is the active environment
|
|
95
|
+
๐ช abc123f ๐
2024-01-15T14-30 # Created timestamp
|
|
96
|
+
๐ช abc123f โ 24h # Time-to-live policy
|
|
97
|
+
๐ช abc123f ๐คก maxime # Requested by maxime (clown emoji!)
|
|
98
|
+
|
|
99
|
+
# When ready:
|
|
100
|
+
๐ช abc123f ๐ฆ running # Environment is now running
|
|
101
|
+
๐ช abc123f ๐ 52-1-2-3 # Available at http://52.1.2.3:8080
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## ๐ Quick Start for Superset Contributors
|
|
105
|
+
|
|
106
|
+
### 1. As a Contributor (Using GitHub Labels)
|
|
107
|
+
|
|
108
|
+
**Create an ephemeral environment:**
|
|
109
|
+
1. Go to your PR in GitHub
|
|
110
|
+
2. Add label: `๐ช trigger-start`
|
|
111
|
+
3. Watch the magic happen - labels will update automatically
|
|
112
|
+
4. When you see `๐ช ๐ฆ {sha} running`, your environment is ready!
|
|
113
|
+
5. Get URL from `๐ช ๐ {sha} {ip}` โ `http://{ip}:8080`
|
|
114
|
+
|
|
115
|
+
**Configure your environment:**
|
|
116
|
+
```bash
|
|
117
|
+
# Add these labels to enable Superset feature flags:
|
|
118
|
+
๐ช conf-enable-ALERTS # Enable alerts feature
|
|
119
|
+
๐ช conf-enable-DASHBOARD_RBAC # Enable dashboard RBAC
|
|
120
|
+
๐ช conf-disable-SSH_TUNNELING # Disable SSH tunneling
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Clean up when done:**
|
|
124
|
+
```bash
|
|
125
|
+
# Add this label:
|
|
126
|
+
๐ช trigger-stop
|
|
127
|
+
# All circus labels disappear, AWS resources cleaned up
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### 2. As a Maintainer (Using CLI)
|
|
131
|
+
|
|
132
|
+
**Install the CLI:**
|
|
133
|
+
```bash
|
|
134
|
+
pip install superset-showtime
|
|
135
|
+
export GITHUB_TOKEN=your_token
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Monitor all environments:**
|
|
139
|
+
```bash
|
|
140
|
+
showtime list # See all active environments
|
|
141
|
+
showtime status 1234 # Check specific PR environment
|
|
142
|
+
showtime labels # Learn the complete label system
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Test and debug:**
|
|
146
|
+
```bash
|
|
147
|
+
showtime start 1234 --dry-run-aws # Test environment creation
|
|
148
|
+
showtime test-lifecycle 1234 # Full workflow simulation
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## ๐ช Complete Label Reference
|
|
152
|
+
|
|
153
|
+
### ๐ฏ Trigger Labels (Add These to Your PR)
|
|
154
|
+
|
|
155
|
+
| Label | Action | Result |
|
|
156
|
+
|-------|---------|---------|
|
|
157
|
+
| `๐ช trigger-start` | Create environment | Builds and deploys ephemeral environment |
|
|
158
|
+
| `๐ช trigger-stop` | Destroy environment | Cleans up AWS resources and removes all labels |
|
|
159
|
+
| `๐ช trigger-sync` | Update to latest commit | Zero-downtime rolling update |
|
|
160
|
+
| `๐ช conf-enable-ALERTS` | Enable feature flag | Sets `SUPERSET_FEATURE_ALERTS=True` |
|
|
161
|
+
| `๐ช conf-disable-DASHBOARD_RBAC` | Disable feature flag | Sets `SUPERSET_FEATURE_DASHBOARD_RBAC=False` |
|
|
162
|
+
|
|
163
|
+
### ๐ State Labels (Automatically Managed)
|
|
164
|
+
|
|
165
|
+
| Label Pattern | Meaning | Example |
|
|
166
|
+
|---------------|---------|---------|
|
|
167
|
+
| `๐ช {sha} ๐ฆ {status}` | Environment status | `๐ช abc123f ๐ฆ running` |
|
|
168
|
+
| `๐ช ๐ฏ {sha}` | Active environment pointer | `๐ช ๐ฏ abc123f` |
|
|
169
|
+
| `๐ช ๐๏ธ {sha}` | Building environment pointer | `๐ช ๐๏ธ def456a` |
|
|
170
|
+
| `๐ช {sha} ๐
{timestamp}` | Creation time | `๐ช abc123f ๐
2024-01-15T14-30` |
|
|
171
|
+
| `๐ช {sha} ๐ {ip-with-dashes}` | Environment IP | `๐ช abc123f ๐ 52-1-2-3` |
|
|
172
|
+
| `๐ช {sha} โ {ttl}` | Time-to-live policy | `๐ช abc123f โ 24h` |
|
|
173
|
+
| `๐ช {sha} ๐คก {username}` | Who requested | `๐ช abc123f ๐คก maxime` |
|
|
174
|
+
| `๐ช {sha} โ๏ธ {config}` | Feature flags enabled | `๐ช abc123f โ๏ธ alerts,debug` |
|
|
175
|
+
|
|
176
|
+
## ๐ Complete Workflows
|
|
177
|
+
|
|
178
|
+
### Creating Your First Environment
|
|
179
|
+
|
|
180
|
+
1. **Add trigger label** in GitHub UI: `๐ช trigger-start`
|
|
181
|
+
2. **Watch state labels appear:**
|
|
182
|
+
```
|
|
183
|
+
๐ช abc123f ๐ฆ building โ Environment is building
|
|
184
|
+
๐ช ๐ฏ abc123f โ This is the active environment
|
|
185
|
+
๐ช abc123f ๐
2024-01-15T14-30 โ Started building at this time
|
|
186
|
+
```
|
|
187
|
+
3. **Wait for completion:**
|
|
188
|
+
```
|
|
189
|
+
๐ช abc123f ๐ฆ running โ Now ready!
|
|
190
|
+
๐ช abc123f ๐ 52-1-2-3 โ Visit http://52.1.2.3:8080
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Enabling Feature Flags
|
|
194
|
+
|
|
195
|
+
1. **Add config label:** `๐ช conf-enable-ALERTS`
|
|
196
|
+
2. **Watch config update:**
|
|
197
|
+
```
|
|
198
|
+
๐ช abc123f โ๏ธ standard โ Before
|
|
199
|
+
๐ช abc123f โ๏ธ alerts โ After (feature enabled!)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Rolling Updates (Automatic!)
|
|
203
|
+
|
|
204
|
+
When you push new commits, Showtime automatically:
|
|
205
|
+
1. **Detects new commit** via GitHub webhook
|
|
206
|
+
2. **Builds new environment** alongside old one
|
|
207
|
+
3. **Switches traffic** when new environment is ready
|
|
208
|
+
4. **Cleans up old environment**
|
|
209
|
+
|
|
210
|
+
You'll see:
|
|
211
|
+
```bash
|
|
212
|
+
# During update:
|
|
213
|
+
๐ช abc123f ๐ฆ running # Old environment still serving
|
|
214
|
+
๐ช def456a ๐ฆ building # New environment building
|
|
215
|
+
๐ช ๐ฏ abc123f # Traffic still on old
|
|
216
|
+
๐ช ๐๏ธ def456a # New one being prepared
|
|
217
|
+
|
|
218
|
+
# After update:
|
|
219
|
+
๐ช def456a ๐ฆ running # New environment live
|
|
220
|
+
๐ช ๐ฏ def456a # Traffic switched
|
|
221
|
+
๐ช def456a ๐ 52-4-5-6 # New IP address
|
|
222
|
+
# All abc123f labels removed automatically
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## ๐ Security & Permissions
|
|
226
|
+
|
|
227
|
+
### Who Can Use This?
|
|
228
|
+
|
|
229
|
+
- **โ
Superset maintainers** (with write access) can add trigger labels
|
|
230
|
+
- **โ External contributors** cannot trigger environments (no write access to add labels)
|
|
231
|
+
- **๐ Secure by design** - only trusted users can create expensive AWS resources
|
|
232
|
+
|
|
233
|
+
### How GitHub Actions Work
|
|
234
|
+
|
|
235
|
+
The new system replaces complex GHA scripts with simple ones:
|
|
236
|
+
|
|
237
|
+
```yaml
|
|
238
|
+
# .github/workflows/circus.yml (replaces current ephemeral-env.yml)
|
|
239
|
+
on:
|
|
240
|
+
pull_request_target:
|
|
241
|
+
types: [labeled, unlabeled, synchronize]
|
|
242
|
+
|
|
243
|
+
jobs:
|
|
244
|
+
circus-handler:
|
|
245
|
+
if: contains(github.event.label.name, '๐ช')
|
|
246
|
+
steps:
|
|
247
|
+
- name: Install Showtime from PyPI
|
|
248
|
+
run: pip install superset-showtime
|
|
249
|
+
|
|
250
|
+
- name: Process circus triggers
|
|
251
|
+
run: python -m showtime handle-trigger ${{ github.event.pull_request.number }}
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
**Security benefits:**
|
|
255
|
+
- **Always runs trusted code** (from PyPI, not PR code)
|
|
256
|
+
- **Simple workflow logic** (just install CLI and run)
|
|
257
|
+
- **Same permission model** as current system
|
|
258
|
+
|
|
259
|
+
## ๐ ๏ธ Installation & Setup
|
|
260
|
+
|
|
261
|
+
### For Contributors (GitHub Labels Only)
|
|
262
|
+
No installation needed! Just use the GitHub label system.
|
|
263
|
+
|
|
264
|
+
### For Maintainers (CLI Access)
|
|
265
|
+
|
|
266
|
+
**Install CLI:**
|
|
267
|
+
```bash
|
|
268
|
+
pip install superset-showtime
|
|
269
|
+
export GITHUB_TOKEN=your_personal_access_token
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**Test CLI:**
|
|
273
|
+
```bash
|
|
274
|
+
showtime list # See all active environments
|
|
275
|
+
showtime status 1234 # Check specific environment
|
|
276
|
+
showtime labels # Learn complete label system
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### For Repository Setup (One-Time)
|
|
280
|
+
|
|
281
|
+
**1. Install GitHub workflows:**
|
|
282
|
+
Copy `.github/workflows/circus.yml` and `.github/workflows/circus-cleanup.yml` to your Superset repo.
|
|
283
|
+
|
|
284
|
+
**2. Add repository secrets:**
|
|
285
|
+
- `AWS_ACCESS_KEY_ID` (already exists)
|
|
286
|
+
- `AWS_SECRET_ACCESS_KEY` (already exists)
|
|
287
|
+
- `GITHUB_TOKEN` (already exists)
|
|
288
|
+
|
|
289
|
+
**3. Replace old workflows:**
|
|
290
|
+
Remove or disable the current `ephemeral-env.yml` and `ephemeral-env-pr-close.yml`.
|
|
291
|
+
|
|
292
|
+
## ๐ CLI Commands Reference
|
|
293
|
+
|
|
294
|
+
### Core Commands
|
|
295
|
+
```bash
|
|
296
|
+
showtime start 1234 # Create environment (with dry-run options)
|
|
297
|
+
showtime stop 1234 # Delete environment
|
|
298
|
+
showtime status 1234 # Show environment status
|
|
299
|
+
showtime list # List all environments across org
|
|
300
|
+
showtime labels # Complete label reference guide
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Testing & Development
|
|
304
|
+
```bash
|
|
305
|
+
showtime start 1234 --dry-run-aws # Mock AWS, real GitHub labels
|
|
306
|
+
showtime test-lifecycle 1234 --real-github # Full workflow simulation
|
|
307
|
+
showtime handle-trigger 1234 --dry-run-aws # Simulate GitHub Actions
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Advanced Operations
|
|
311
|
+
```bash
|
|
312
|
+
showtime cleanup --older-than 48h # Clean up old environments
|
|
313
|
+
showtime list --status running --user maxime # Filter environments
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
## ๐ช Benefits for Superset
|
|
317
|
+
|
|
318
|
+
### For Contributors
|
|
319
|
+
- **๐ฏ Simple workflow** - Just add/remove GitHub labels
|
|
320
|
+
- **๐ Visual feedback** - See environment status in PR labels
|
|
321
|
+
- **โก Automatic updates** - New commits update environments automatically
|
|
322
|
+
- **๐ง Live configuration** - Enable/disable feature flags without rebuilding
|
|
323
|
+
|
|
324
|
+
### For Maintainers
|
|
325
|
+
- **๐ Complete visibility** - `showtime list` shows all environments
|
|
326
|
+
- **๐งน Easy cleanup** - Automatic expired environment cleanup
|
|
327
|
+
- **๐ Better debugging** - Clear state in labels, comprehensive CLI
|
|
328
|
+
- **๐ฐ Cost savings** - No duplicate environments, proper cleanup
|
|
329
|
+
|
|
330
|
+
### For Operations
|
|
331
|
+
- **๐ Simpler workflows** - Replace complex GHA scripts with simple CLI calls
|
|
332
|
+
- **๐ Same security model** - No new permissions needed
|
|
333
|
+
- **๐ฏ Deterministic** - Predictable AWS resource naming
|
|
334
|
+
- **๐จ Monitoring ready** - 48h maximum lifetime, scheduled cleanup
|
|
335
|
+
|
|
336
|
+
## ๐๏ธ Architecture
|
|
337
|
+
|
|
338
|
+
### State Management
|
|
339
|
+
All state lives in **GitHub labels** - no external databases needed:
|
|
340
|
+
- **Trigger labels** (`๐ช trigger-*`) - Commands that get processed and removed
|
|
341
|
+
- **State labels** (`๐ช ๐ฆ *`) - Current environment status, managed by CLI
|
|
342
|
+
|
|
343
|
+
### AWS Resources
|
|
344
|
+
Deterministic naming enables reliable cleanup:
|
|
345
|
+
- **ECS Service:** `pr-{pr_number}-{sha}` (e.g., `pr-1234-abc123f`)
|
|
346
|
+
- **ECR Image:** `pr-{pr_number}-{sha}-ci` (e.g., `pr-1234-abc123f-ci`)
|
|
347
|
+
|
|
348
|
+
### Rolling Updates
|
|
349
|
+
Zero-downtime updates by running multiple environments:
|
|
350
|
+
1. Keep old environment serving traffic
|
|
351
|
+
2. Build new environment in parallel
|
|
352
|
+
3. Switch traffic when new environment is healthy
|
|
353
|
+
4. Clean up old environment
|
|
354
|
+
|
|
355
|
+
## ๐ค Contributing
|
|
356
|
+
|
|
357
|
+
### Testing Your Changes
|
|
358
|
+
|
|
359
|
+
**Test with real PRs safely:**
|
|
360
|
+
```bash
|
|
361
|
+
# Test label management without AWS costs:
|
|
362
|
+
showtime start YOUR_PR_NUMBER --dry-run-aws --aws-sleep 10
|
|
363
|
+
|
|
364
|
+
# Test full lifecycle:
|
|
365
|
+
showtime test-lifecycle YOUR_PR_NUMBER --real-github
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Development Setup
|
|
369
|
+
|
|
370
|
+
```bash
|
|
371
|
+
git clone https://github.com/mistercrunch/superset-showtime
|
|
372
|
+
cd superset-showtime
|
|
373
|
+
|
|
374
|
+
# Using uv (recommended):
|
|
375
|
+
uv pip install -e ".[dev]"
|
|
376
|
+
make pre-commit
|
|
377
|
+
make test
|
|
378
|
+
|
|
379
|
+
# Traditional pip:
|
|
380
|
+
pip install -e ".[dev]"
|
|
381
|
+
pre-commit install
|
|
382
|
+
pytest
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
## ๐ License
|
|
386
|
+
|
|
387
|
+
Apache License 2.0 - same as Apache Superset.
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
**๐ช "Ladies and gentlemen, welcome to Superset Showtime - where ephemeral environments are always under the big top!"** ๐ช๐คกโจ
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
showtime/__init__.py,sha256=vyBFCZ0eKSRhMQAI7O2Qkwiv7-eAkiSjkg6RHIBIU1U,482
|
|
2
|
+
showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
|
|
3
|
+
showtime/cli.py,sha256=CXV3HNkwijdUla92txSy6XVV-M925PxVK6jIfMzIsQc,54440
|
|
4
|
+
showtime/commands/__init__.py,sha256=M2wn5hYgwNCryMjLT79ncobvK884r-xk3znkCmINN_0,28
|
|
5
|
+
showtime/commands/start.py,sha256=DPGbgvGPh7I60LK_VioDljUhdmhNFVjEy6BchFv1lCo,1026
|
|
6
|
+
showtime/core/__init__.py,sha256=54hbdFNGrzuNMBdraezfjT8Zi6g221pKlJ9mREnKwCw,34
|
|
7
|
+
showtime/core/aws.py,sha256=GXJc3h55J5n5czjqkJpWLhvLMCdcZjZaOdVSsdvu7PQ,30008
|
|
8
|
+
showtime/core/circus.py,sha256=avvcFqvHRKo2o_Yxm0V1jVRMF2RSLABTqhe_nMO3GAM,9504
|
|
9
|
+
showtime/core/config.py,sha256=jISjWE79vFaIX1nqeNhRfs_jM5Xoe-p7LTmJzT1OsSI,4348
|
|
10
|
+
showtime/core/emojis.py,sha256=iq2EginrCKN_oMh3XmZF9SgbMNSXV6e-JgwUAtRwUuM,2430
|
|
11
|
+
showtime/core/github.py,sha256=U8H9tudJjmHVr1aWKDd7o5AJkmgUnX_AIC2lqh4voTg,7967
|
|
12
|
+
showtime/data/ecs-task-definition.json,sha256=r1cgkZJ2SS1gul-WFuV-QlzdFjr5n3g-kdKka79N-Jk,1687
|
|
13
|
+
superset_showtime-0.1.0.dist-info/METADATA,sha256=_gobO4ZLHlyTeyOrwhzSBgxLIHcElL6M0p8mc0Pb4Zs,13938
|
|
14
|
+
superset_showtime-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
superset_showtime-0.1.0.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
|
|
16
|
+
superset_showtime-0.1.0.dist-info/RECORD,,
|