superset-showtime 0.2.8__py3-none-any.whl → 0.4.2__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 +3 -2
- showtime/cli.py +276 -1159
- showtime/core/aws.py +30 -26
- showtime/core/github.py +10 -6
- showtime/core/github_messages.py +16 -13
- showtime/core/pull_request.py +536 -0
- showtime/core/show.py +279 -0
- {superset_showtime-0.2.8.dist-info → superset_showtime-0.4.2.dist-info}/METADATA +56 -121
- superset_showtime-0.4.2.dist-info/RECORD +16 -0
- showtime/commands/__init__.py +0 -1
- showtime/commands/start.py +0 -40
- showtime/core/circus.py +0 -279
- superset_showtime-0.2.8.dist-info/RECORD +0 -17
- {superset_showtime-0.2.8.dist-info → superset_showtime-0.4.2.dist-info}/WHEEL +0 -0
- {superset_showtime-0.2.8.dist-info → superset_showtime-0.4.2.dist-info}/entry_points.txt +0 -0
showtime/core/show.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
🎪 Show class - Individual ephemeral environment management
|
|
3
|
+
|
|
4
|
+
Single environment operations: Docker build, AWS deployment, state transitions.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
# Import interfaces for singleton access
|
|
13
|
+
# Note: These will be imported when the module loads, creating singletons
|
|
14
|
+
def get_interfaces(): # type: ignore
|
|
15
|
+
"""Lazy-load interfaces to avoid circular imports"""
|
|
16
|
+
from .aws import AWSInterface
|
|
17
|
+
from .github import GitHubInterface
|
|
18
|
+
|
|
19
|
+
return GitHubInterface(), AWSInterface()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class Show:
|
|
24
|
+
"""Single ephemeral environment state from circus labels"""
|
|
25
|
+
|
|
26
|
+
pr_number: int
|
|
27
|
+
sha: str # 7-char commit SHA
|
|
28
|
+
status: str # building, built, deploying, running, updating, failed
|
|
29
|
+
ip: Optional[str] = None # Environment IP address
|
|
30
|
+
created_at: Optional[str] = None # ISO timestamp
|
|
31
|
+
ttl: str = "24h" # 24h, 48h, close, etc.
|
|
32
|
+
requested_by: Optional[str] = None # GitHub username
|
|
33
|
+
|
|
34
|
+
@property
|
|
35
|
+
def aws_service_name(self) -> str:
|
|
36
|
+
"""Deterministic ECS service name: pr-{pr_number}-{sha}"""
|
|
37
|
+
return f"pr-{self.pr_number}-{self.sha}"
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def ecs_service_name(self) -> str:
|
|
41
|
+
"""ECS service name with -service suffix"""
|
|
42
|
+
return f"{self.aws_service_name}-service"
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def aws_image_tag(self) -> str:
|
|
46
|
+
"""Deterministic Docker image tag: pr-{pr_number}-{sha}-ci"""
|
|
47
|
+
return f"pr-{self.pr_number}-{self.sha}-ci"
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def short_sha(self) -> str:
|
|
51
|
+
"""Return the short SHA (already short)"""
|
|
52
|
+
return self.sha
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def is_running(self) -> bool:
|
|
56
|
+
"""Check if environment is currently running"""
|
|
57
|
+
return self.status == "running"
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def is_building(self) -> bool:
|
|
61
|
+
"""Check if environment is currently building"""
|
|
62
|
+
return self.status == "building"
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def is_built(self) -> bool:
|
|
66
|
+
"""Check if environment is built (Docker complete, ready for deploy)"""
|
|
67
|
+
return self.status == "built"
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def is_deploying(self) -> bool:
|
|
71
|
+
"""Check if environment is currently deploying to AWS"""
|
|
72
|
+
return self.status == "deploying"
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def is_updating(self) -> bool:
|
|
76
|
+
"""Check if environment is currently updating"""
|
|
77
|
+
return self.status == "updating"
|
|
78
|
+
|
|
79
|
+
def needs_update(self, latest_sha: str) -> bool:
|
|
80
|
+
"""Check if environment needs update to latest SHA"""
|
|
81
|
+
return self.sha != latest_sha[:7]
|
|
82
|
+
|
|
83
|
+
def is_expired(self, max_age_hours: int) -> bool:
|
|
84
|
+
"""Check if this environment is expired based on age"""
|
|
85
|
+
if not self.created_at:
|
|
86
|
+
return False
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
from datetime import datetime, timedelta
|
|
90
|
+
|
|
91
|
+
created_time = datetime.fromisoformat(self.created_at.replace("-", ":"))
|
|
92
|
+
expiry_time = created_time + timedelta(hours=max_age_hours)
|
|
93
|
+
return datetime.now() > expiry_time
|
|
94
|
+
except (ValueError, AttributeError):
|
|
95
|
+
return False # If we can't parse, assume not expired
|
|
96
|
+
|
|
97
|
+
def to_circus_labels(self) -> List[str]:
|
|
98
|
+
"""Convert show state to circus tent emoji labels (per-SHA format)"""
|
|
99
|
+
if not self.created_at:
|
|
100
|
+
self.created_at = datetime.utcnow().strftime("%Y-%m-%dT%H-%M")
|
|
101
|
+
|
|
102
|
+
labels = [
|
|
103
|
+
f"🎪 {self.sha} 🚦 {self.status}", # SHA-first status
|
|
104
|
+
f"🎪 🎯 {self.sha}", # Active pointer (no value)
|
|
105
|
+
f"🎪 {self.sha} 📅 {self.created_at}", # SHA-first timestamp
|
|
106
|
+
f"🎪 {self.sha} ⌛ {self.ttl}", # SHA-first TTL
|
|
107
|
+
]
|
|
108
|
+
|
|
109
|
+
if self.ip:
|
|
110
|
+
labels.append(f"🎪 {self.sha} 🌐 {self.ip}:8080")
|
|
111
|
+
|
|
112
|
+
if self.requested_by:
|
|
113
|
+
labels.append(f"🎪 {self.sha} 🤡 {self.requested_by}")
|
|
114
|
+
|
|
115
|
+
return labels
|
|
116
|
+
|
|
117
|
+
def build_docker(self, dry_run: bool = False) -> None:
|
|
118
|
+
"""Build Docker image for this environment (atomic operation)"""
|
|
119
|
+
if not dry_run:
|
|
120
|
+
self._build_docker_image() # Raises on failure
|
|
121
|
+
|
|
122
|
+
def deploy_aws(self, dry_run: bool = False) -> None:
|
|
123
|
+
"""Deploy to AWS (atomic operation)"""
|
|
124
|
+
github, aws = get_interfaces()
|
|
125
|
+
|
|
126
|
+
if not dry_run:
|
|
127
|
+
result = aws.create_environment(
|
|
128
|
+
pr_number=self.pr_number,
|
|
129
|
+
sha=self.sha + "0" * (40 - len(self.sha)), # Convert to full SHA
|
|
130
|
+
github_user=self.requested_by or "unknown",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if not result.success:
|
|
134
|
+
raise Exception(f"AWS deployment failed: {result.error}")
|
|
135
|
+
|
|
136
|
+
# Update with deployment results
|
|
137
|
+
self.ip = result.ip
|
|
138
|
+
else:
|
|
139
|
+
# Mock successful deployment for dry-run
|
|
140
|
+
self.ip = "52.1.2.3"
|
|
141
|
+
|
|
142
|
+
def stop(self, dry_run_github: bool = False, dry_run_aws: bool = False) -> None:
|
|
143
|
+
"""Stop this environment (cleanup AWS resources)
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
Exception: On cleanup failure
|
|
147
|
+
"""
|
|
148
|
+
github, aws = get_interfaces()
|
|
149
|
+
|
|
150
|
+
# Delete AWS resources (pure technical work)
|
|
151
|
+
if not dry_run_aws:
|
|
152
|
+
success = aws.delete_environment(self.aws_service_name, self.pr_number)
|
|
153
|
+
if not success:
|
|
154
|
+
raise Exception(f"Failed to delete AWS service: {self.aws_service_name}")
|
|
155
|
+
|
|
156
|
+
# No comments - PullRequest handles that!
|
|
157
|
+
|
|
158
|
+
def _build_docker_image(self) -> None:
|
|
159
|
+
"""Build Docker image for this environment"""
|
|
160
|
+
import os
|
|
161
|
+
import platform
|
|
162
|
+
import subprocess
|
|
163
|
+
|
|
164
|
+
tag = f"apache/superset:pr-{self.pr_number}-{self.sha}-ci"
|
|
165
|
+
|
|
166
|
+
# Detect if running in CI environment
|
|
167
|
+
is_ci = bool(os.getenv("GITHUB_ACTIONS") or os.getenv("CI"))
|
|
168
|
+
|
|
169
|
+
# Base command
|
|
170
|
+
cmd = [
|
|
171
|
+
"docker",
|
|
172
|
+
"buildx",
|
|
173
|
+
"build",
|
|
174
|
+
"--push",
|
|
175
|
+
"--platform",
|
|
176
|
+
"linux/amd64",
|
|
177
|
+
"--target",
|
|
178
|
+
"ci",
|
|
179
|
+
"--build-arg",
|
|
180
|
+
"INCLUDE_CHROMIUM=false",
|
|
181
|
+
"--build-arg",
|
|
182
|
+
"LOAD_EXAMPLES_DUCKDB=true",
|
|
183
|
+
"-t",
|
|
184
|
+
tag,
|
|
185
|
+
".",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
# Add caching based on environment
|
|
189
|
+
if is_ci:
|
|
190
|
+
# Full registry caching in CI (Docker driver supports it)
|
|
191
|
+
cmd.extend([
|
|
192
|
+
"--cache-from",
|
|
193
|
+
"type=registry,ref=apache/superset-cache:3.10-slim-bookworm",
|
|
194
|
+
"--cache-to",
|
|
195
|
+
"type=registry,mode=max,ref=apache/superset-cache:3.10-slim-bookworm",
|
|
196
|
+
])
|
|
197
|
+
print("🐳 CI environment: Using full registry caching")
|
|
198
|
+
else:
|
|
199
|
+
# Local build: cache-from only (no cache export)
|
|
200
|
+
cmd.extend([
|
|
201
|
+
"--cache-from",
|
|
202
|
+
"type=registry,ref=apache/superset-cache:3.10-slim-bookworm",
|
|
203
|
+
])
|
|
204
|
+
print("🐳 Local environment: Using cache-from only (no export)")
|
|
205
|
+
|
|
206
|
+
# Add --load only when building for native architecture or explicitly requested
|
|
207
|
+
# Intel Mac/Linux can load linux/amd64, Apple Silicon cannot
|
|
208
|
+
native_x86 = platform.machine() in ("x86_64", "AMD64")
|
|
209
|
+
force_load = os.getenv("DOCKER_LOAD", "false").lower() == "true"
|
|
210
|
+
|
|
211
|
+
if native_x86 or force_load:
|
|
212
|
+
cmd.insert(-1, "--load") # Insert before the "." argument
|
|
213
|
+
print("🐳 Will load image to local Docker daemon (native x86_64 platform)")
|
|
214
|
+
else:
|
|
215
|
+
print("🐳 Cross-platform build - pushing to registry only (no local load)")
|
|
216
|
+
|
|
217
|
+
print(f"🐳 Building Docker image: {tag}")
|
|
218
|
+
|
|
219
|
+
# Stream output in real-time
|
|
220
|
+
process = subprocess.Popen(
|
|
221
|
+
cmd,
|
|
222
|
+
stdout=subprocess.PIPE,
|
|
223
|
+
stderr=subprocess.STDOUT,
|
|
224
|
+
text=True,
|
|
225
|
+
bufsize=1,
|
|
226
|
+
universal_newlines=True,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if process.stdout:
|
|
230
|
+
for line in process.stdout:
|
|
231
|
+
print(f"🐳 {line.rstrip()}")
|
|
232
|
+
|
|
233
|
+
return_code = process.wait(timeout=3600)
|
|
234
|
+
if return_code != 0:
|
|
235
|
+
raise Exception(f"Docker build failed with exit code: {return_code}")
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def from_circus_labels(cls, pr_number: int, labels: List[str], sha: str) -> Optional["Show"]:
|
|
239
|
+
"""Create Show from circus tent labels for specific SHA"""
|
|
240
|
+
show_data = {
|
|
241
|
+
"pr_number": pr_number,
|
|
242
|
+
"sha": sha,
|
|
243
|
+
"status": "building", # default
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for label in labels:
|
|
247
|
+
if not label.startswith("🎪"):
|
|
248
|
+
continue
|
|
249
|
+
|
|
250
|
+
parts = label.split(" ")
|
|
251
|
+
if len(parts) < 3:
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
# Per-SHA format: 🎪 {sha} {emoji} {value}
|
|
255
|
+
if parts[1] == sha: # This label is for our SHA
|
|
256
|
+
emoji = parts[2]
|
|
257
|
+
value = " ".join(parts[3:]) if len(parts) > 3 else ""
|
|
258
|
+
|
|
259
|
+
if emoji == "🚦": # Status
|
|
260
|
+
show_data["status"] = value
|
|
261
|
+
elif emoji == "📅": # Timestamp
|
|
262
|
+
show_data["created_at"] = value
|
|
263
|
+
elif emoji == "🌐": # IP with port
|
|
264
|
+
show_data["ip"] = value.replace(":8080", "") # Remove port for storage
|
|
265
|
+
elif emoji == "⌛": # TTL
|
|
266
|
+
show_data["ttl"] = value
|
|
267
|
+
elif emoji == "🤡": # User (clown!)
|
|
268
|
+
show_data["requested_by"] = value
|
|
269
|
+
|
|
270
|
+
# Only return Show if we found relevant labels for this SHA
|
|
271
|
+
if any(label.endswith(f" {sha}") for label in labels if "🎯" in label or "🏗️" in label):
|
|
272
|
+
return cls(**show_data) # type: ignore[arg-type]
|
|
273
|
+
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def short_sha(full_sha: str) -> str:
|
|
278
|
+
"""Convert full SHA to short SHA (7 chars)"""
|
|
279
|
+
return full_sha[:7]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: superset-showtime
|
|
3
|
-
Version: 0.2
|
|
3
|
+
Version: 0.4.2
|
|
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/
|
|
@@ -49,6 +49,7 @@ Provides-Extra: azure
|
|
|
49
49
|
Requires-Dist: azure-mgmt-containerinstance>=10.0.0; extra == 'azure'
|
|
50
50
|
Requires-Dist: azure-storage-blob>=12.0.0; extra == 'azure'
|
|
51
51
|
Provides-Extra: dev
|
|
52
|
+
Requires-Dist: boto3-stubs[ec2,ecr,ecs]>=1.40.0; extra == 'dev'
|
|
52
53
|
Requires-Dist: build>=1.0.0; extra == 'dev'
|
|
53
54
|
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
54
55
|
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
|
|
@@ -113,9 +114,34 @@ Superset Showtime is a CLI tool designed primarily for **GitHub Actions** to man
|
|
|
113
114
|
🎪 abc123f 🌐 52-1-2-3 # Available at http://52.1.2.3:8080
|
|
114
115
|
```
|
|
115
116
|
|
|
116
|
-
|
|
117
|
+
### 🔄 Showtime Workflow
|
|
118
|
+
|
|
119
|
+
```mermaid
|
|
120
|
+
flowchart TD
|
|
121
|
+
A[User adds 🎪 ⚡ trigger-start] --> B[GitHub Actions: sync]
|
|
122
|
+
B --> C{Current state?}
|
|
123
|
+
|
|
124
|
+
C -->|No environment| D[🔒 Claim: Remove trigger + Set building]
|
|
125
|
+
C -->|Running + new SHA| E[🔒 Claim: Remove trigger + Set building]
|
|
126
|
+
C -->|Already building| F[❌ Exit: Another job active]
|
|
127
|
+
C -->|No triggers| G[❌ Exit: Nothing to do]
|
|
128
|
+
|
|
129
|
+
D --> H[📋 State: building]
|
|
130
|
+
E --> H
|
|
131
|
+
H --> I[🐳 Docker build]
|
|
132
|
+
I -->|Success| J[📋 State: built]
|
|
133
|
+
I -->|Fail| K[📋 State: failed]
|
|
134
|
+
|
|
135
|
+
J --> L[📋 State: deploying]
|
|
136
|
+
L --> M[☁️ AWS Deploy]
|
|
137
|
+
M -->|Success| N[📋 State: running]
|
|
138
|
+
M -->|Fail| O[📋 State: failed]
|
|
139
|
+
|
|
140
|
+
N --> P[🎪 Environment ready!]
|
|
141
|
+
|
|
142
|
+
Q[User adds 🎪 🛑 trigger-stop] --> R[🧹 Cleanup AWS + Remove labels]
|
|
143
|
+
```
|
|
117
144
|
|
|
118
|
-
> **Note**: CLI is mainly for debugging or developing Showtime itself. Primary interface is GitHub labels above.
|
|
119
145
|
|
|
120
146
|
**Install CLI for debugging:**
|
|
121
147
|
```bash
|
|
@@ -132,11 +158,11 @@ showtime labels # Complete label reference
|
|
|
132
158
|
|
|
133
159
|
**Testing/development:**
|
|
134
160
|
```bash
|
|
135
|
-
showtime
|
|
136
|
-
showtime
|
|
161
|
+
showtime sync 1234 --dry-run-aws --dry-run-docker # Test without costs
|
|
162
|
+
showtime cleanup --dry-run --older-than 1h # Test cleanup logic
|
|
137
163
|
```
|
|
138
164
|
|
|
139
|
-
> **
|
|
165
|
+
> **Architecture**: This CLI implements ACID-style atomic transactions with direct Docker integration. It handles complete environment lifecycle from Docker build to AWS deployment with race condition prevention.
|
|
140
166
|
|
|
141
167
|
## 🎪 Complete Label Reference
|
|
142
168
|
|
|
@@ -229,129 +255,38 @@ You'll see:
|
|
|
229
255
|
|
|
230
256
|
### GitHub Actions Integration
|
|
231
257
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
```yaml
|
|
235
|
-
# .github/workflows/showtime.yml - Integrates with Superset's existing build workflows
|
|
236
|
-
on:
|
|
237
|
-
pull_request_target:
|
|
238
|
-
types: [labeled, unlabeled, synchronize]
|
|
258
|
+
**🎯 Live Workflow**: [showtime-trigger.yml](https://github.com/apache/superset/actions/workflows/showtime-trigger.yml)
|
|
239
259
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
run: pip install superset-showtime
|
|
260
|
+
**How it works:**
|
|
261
|
+
- Triggers on PR label changes, commits, and closures
|
|
262
|
+
- Installs `superset-showtime` from PyPI (trusted code, not PR code)
|
|
263
|
+
- Runs `showtime sync` to handle trigger processing and deployments
|
|
264
|
+
- Supports manual testing via `workflow_dispatch` with specific SHA override
|
|
246
265
|
|
|
247
|
-
|
|
248
|
-
run: python -m showtime handle-trigger ${{ github.event.pull_request.number }}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
**Integration approach:**
|
|
252
|
-
- **Coordinates with Superset builds** - Uses existing container build workflows
|
|
253
|
-
- **Runs trusted code** (from PyPI, not PR code)
|
|
254
|
-
- **Simple orchestration logic** (install CLI and run commands)
|
|
255
|
-
- **Leverages existing infrastructure** - Same AWS resources and permissions
|
|
256
|
-
|
|
257
|
-
## 🛠️ Installation & Setup
|
|
258
|
-
|
|
259
|
-
### For Contributors (GitHub Labels Only)
|
|
260
|
-
No installation needed! Just use GitHub labels to trigger environments.
|
|
261
|
-
|
|
262
|
-
### For Maintainers (Manual CLI Operations)
|
|
263
|
-
|
|
264
|
-
**Install CLI for debugging/testing:**
|
|
266
|
+
**Commands used:**
|
|
265
267
|
```bash
|
|
266
|
-
|
|
267
|
-
|
|
268
|
+
showtime sync PR_NUMBER --check-only # Determine build_needed + target_sha
|
|
269
|
+
showtime sync PR_NUMBER --sha SHA # Execute atomic claim + build + deploy
|
|
268
270
|
```
|
|
269
271
|
|
|
270
|
-
|
|
271
|
-
```bash
|
|
272
|
-
showtime list # Monitor all active environments
|
|
273
|
-
showtime status 1234 # Debug specific environment
|
|
274
|
-
showtime labels # Reference complete label system
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### For Repository Integration (GitHub Actions)
|
|
278
|
-
|
|
279
|
-
**1. Install GitHub workflows:**
|
|
280
|
-
Copy `workflows-reference/showtime-trigger.yml` and `workflows-reference/showtime-cleanup.yml` to Superset's `.github/workflows/`.
|
|
281
|
-
|
|
282
|
-
**2. Configure secrets (already exist in Superset):**
|
|
283
|
-
- `AWS_ACCESS_KEY_ID`
|
|
284
|
-
- `AWS_SECRET_ACCESS_KEY`
|
|
285
|
-
- `GITHUB_TOKEN`
|
|
286
|
-
|
|
287
|
-
**3. Dependencies:**
|
|
288
|
-
Showtime coordinates with Superset's existing build infrastructure - no additional setup needed.
|
|
272
|
+
## 🛠️ CLI Usage
|
|
289
273
|
|
|
290
|
-
|
|
274
|
+
The CLI is primarily used by GitHub Actions, but available for debugging and advanced users:
|
|
291
275
|
|
|
292
|
-
> **Primary Interface**: Use GitHub labels in PR interface. CLI is mainly for maintainers debugging or developing Showtime itself.
|
|
293
|
-
|
|
294
|
-
### Debugging Commands
|
|
295
276
|
```bash
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
showtime labels # Complete label reference
|
|
299
|
-
showtime test-lifecycle 1234 # Full workflow simulation
|
|
300
|
-
```
|
|
277
|
+
pip install superset-showtime
|
|
278
|
+
export GITHUB_TOKEN=your_token
|
|
301
279
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
showtime start
|
|
305
|
-
showtime
|
|
306
|
-
showtime
|
|
307
|
-
showtime
|
|
308
|
-
showtime cleanup --
|
|
280
|
+
# Core commands:
|
|
281
|
+
showtime sync PR_NUMBER # Sync to desired state (main command)
|
|
282
|
+
showtime start PR_NUMBER # Create new environment
|
|
283
|
+
showtime stop PR_NUMBER # Delete environment
|
|
284
|
+
showtime status PR_NUMBER # Show current state
|
|
285
|
+
showtime list # List all environments
|
|
286
|
+
showtime cleanup --older-than 48h # Clean up expired environments
|
|
309
287
|
```
|
|
310
288
|
|
|
311
|
-
### GitHub Actions Commands
|
|
312
|
-
```bash
|
|
313
|
-
showtime handle-trigger 1234 # Process trigger labels (called by GHA)
|
|
314
|
-
showtime cleanup --older-than 48h # Scheduled cleanup (called by GHA)
|
|
315
|
-
```
|
|
316
289
|
|
|
317
|
-
## 🎪 Benefits for Superset
|
|
318
|
-
|
|
319
|
-
### For Contributors
|
|
320
|
-
- **🎯 Simple workflow** - Just add/remove GitHub labels
|
|
321
|
-
- **👀 Visual feedback** - See environment status in PR labels
|
|
322
|
-
- **⚡ Automatic updates** - New commits update environments automatically
|
|
323
|
-
- **🔧 Configuration testing** - Test config changes through code commits
|
|
324
|
-
|
|
325
|
-
### For Maintainers
|
|
326
|
-
- **📊 Complete visibility** - `showtime list` shows all environments
|
|
327
|
-
- **🧹 Easy cleanup** - Automatic expired environment cleanup
|
|
328
|
-
- **🔍 Better debugging** - Clear state in labels, comprehensive CLI
|
|
329
|
-
- **💰 Cost savings** - No duplicate environments, proper cleanup
|
|
330
|
-
|
|
331
|
-
### For Operations
|
|
332
|
-
- **📝 Simpler workflows** - Replace complex GHA scripts with simple CLI calls
|
|
333
|
-
- **🔒 Same security model** - No new permissions needed
|
|
334
|
-
- **🎯 Deterministic** - Predictable AWS resource naming
|
|
335
|
-
- **🚨 Monitoring ready** - 48h maximum lifetime, scheduled cleanup
|
|
336
|
-
|
|
337
|
-
## 🏗️ Architecture
|
|
338
|
-
|
|
339
|
-
### State Management
|
|
340
|
-
All state lives in **GitHub labels** - no external databases needed:
|
|
341
|
-
- **Trigger labels** (`🎪 trigger-*`) - Commands that get processed and removed
|
|
342
|
-
- **State labels** (`🎪 🚦 *`) - Current environment status, managed by CLI
|
|
343
|
-
|
|
344
|
-
### AWS Resources
|
|
345
|
-
Deterministic naming enables reliable cleanup:
|
|
346
|
-
- **ECS Service:** `pr-{pr_number}-{sha}` (e.g., `pr-1234-abc123f`)
|
|
347
|
-
- **ECR Image:** `pr-{pr_number}-{sha}-ci` (e.g., `pr-1234-abc123f-ci`)
|
|
348
|
-
|
|
349
|
-
### Rolling Updates
|
|
350
|
-
Zero-downtime updates by running multiple environments:
|
|
351
|
-
1. Keep old environment serving traffic
|
|
352
|
-
2. Build new environment in parallel
|
|
353
|
-
3. Switch traffic when new environment is healthy
|
|
354
|
-
4. Clean up old environment
|
|
355
290
|
|
|
356
291
|
## 🤝 Contributing
|
|
357
292
|
|
|
@@ -359,11 +294,11 @@ Zero-downtime updates by running multiple environments:
|
|
|
359
294
|
|
|
360
295
|
**Test with real PRs safely:**
|
|
361
296
|
```bash
|
|
362
|
-
# Test
|
|
363
|
-
showtime
|
|
297
|
+
# Test full workflow without costs:
|
|
298
|
+
showtime sync YOUR_PR_NUMBER --dry-run-aws --dry-run-docker
|
|
364
299
|
|
|
365
|
-
# Test
|
|
366
|
-
showtime
|
|
300
|
+
# Test cleanup logic:
|
|
301
|
+
showtime cleanup --dry-run --older-than 24h
|
|
367
302
|
```
|
|
368
303
|
|
|
369
304
|
### Development Setup
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
showtime/__init__.py,sha256=jzoTTK9K3uRo5GuAPaej-89Ny35OFYmHBR3FTQ7MiVk,448
|
|
2
|
+
showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
|
|
3
|
+
showtime/cli.py,sha256=faFM6pe3gz49_1KrzUeri7dQffqz4WP92JmGxPaIOC0,25249
|
|
4
|
+
showtime/core/__init__.py,sha256=54hbdFNGrzuNMBdraezfjT8Zi6g221pKlJ9mREnKwCw,34
|
|
5
|
+
showtime/core/aws.py,sha256=REeZ6_1C9f6mBchBAGa1MeDJeZIwir4IJ92HLRcK5ok,32636
|
|
6
|
+
showtime/core/emojis.py,sha256=MHEDuPIdfNiop4zbNLuviz3eY05QiftYSHHCVbkfKhw,2129
|
|
7
|
+
showtime/core/github.py,sha256=uETvKDO2Yhpqg3fxLtrKaCuZR3b-1LVmgnf5aLcqrAQ,9988
|
|
8
|
+
showtime/core/github_messages.py,sha256=MfgwCukrEsWWesMsuL8saciDgP4nS-gijzu8DXr-Alg,7450
|
|
9
|
+
showtime/core/label_colors.py,sha256=efhbFnz_3nqEnEqmgyF6_hZbxtCu_fmb68BIIUpSsnk,3895
|
|
10
|
+
showtime/core/pull_request.py,sha256=Yxc7Oy0dkvQQm3DbwUKbiCAW0wEivM8U2j5vSRwIDxE,20475
|
|
11
|
+
showtime/core/show.py,sha256=BRMH_Z53UfK0VXAMHfzS0u7_s0h634VKTMzWMniRNsg,9759
|
|
12
|
+
showtime/data/ecs-task-definition.json,sha256=0ZaE0FZ8IWduXd2RyscMhXeVgxyym6qtjH02CK9mXBI,2235
|
|
13
|
+
superset_showtime-0.4.2.dist-info/METADATA,sha256=h_RweT3LG7UW7HLTx49Gv26ulSJUV2c-YVULpYm_gw8,12052
|
|
14
|
+
superset_showtime-0.4.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
15
|
+
superset_showtime-0.4.2.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
|
|
16
|
+
superset_showtime-0.4.2.dist-info/RECORD,,
|
showtime/commands/__init__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Showtime CLI commands"""
|
showtime/commands/start.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
🎪 Start show command - Create ephemeral environments
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
from dataclasses import dataclass
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclass
|
|
10
|
-
class StartResult:
|
|
11
|
-
"""Result of starting a show"""
|
|
12
|
-
|
|
13
|
-
success: bool
|
|
14
|
-
url: Optional[str] = None
|
|
15
|
-
error: Optional[str] = None
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def start_show(
|
|
19
|
-
pr_number: int, sha: Optional[str] = None, ttl: str = "24h", size: str = "standard"
|
|
20
|
-
) -> StartResult:
|
|
21
|
-
"""
|
|
22
|
-
Start the show! Create ephemeral environment for PR
|
|
23
|
-
|
|
24
|
-
Args:
|
|
25
|
-
pr_number: PR number to create environment for
|
|
26
|
-
sha: Specific commit SHA (default: latest)
|
|
27
|
-
ttl: Time to live (24h, 48h, 1w, close)
|
|
28
|
-
size: Environment size (standard, large)
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
StartResult with success status and details
|
|
32
|
-
"""
|
|
33
|
-
# TODO: Implement environment creation
|
|
34
|
-
# 1. Get latest SHA if not provided
|
|
35
|
-
# 2. Create circus labels
|
|
36
|
-
# 3. Build Docker image
|
|
37
|
-
# 4. Deploy to ECS
|
|
38
|
-
# 5. Update labels with running state
|
|
39
|
-
|
|
40
|
-
return StartResult(success=False, error="Not yet implemented")
|