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/circus.py
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
🎪 Circus tent emoji label parsing and state management
|
|
3
|
-
|
|
4
|
-
Core logic for parsing GitHub labels with circus tent emoji patterns.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from datetime import datetime
|
|
9
|
-
from typing import TYPE_CHECKING, List, Optional
|
|
10
|
-
|
|
11
|
-
if TYPE_CHECKING:
|
|
12
|
-
from .github import GitHubInterface
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@dataclass
|
|
16
|
-
class Show:
|
|
17
|
-
"""Single ephemeral environment state from circus labels"""
|
|
18
|
-
|
|
19
|
-
pr_number: int
|
|
20
|
-
sha: str # 7-char commit SHA
|
|
21
|
-
status: str # building, running, updating, failed
|
|
22
|
-
ip: Optional[str] = None # Environment IP address
|
|
23
|
-
created_at: Optional[str] = None # ISO timestamp
|
|
24
|
-
ttl: str = "24h" # 24h, 48h, close, etc.
|
|
25
|
-
requested_by: Optional[str] = None # GitHub username
|
|
26
|
-
|
|
27
|
-
@property
|
|
28
|
-
def aws_service_name(self) -> str:
|
|
29
|
-
"""Deterministic ECS service name: pr-{pr_number}-{sha}"""
|
|
30
|
-
return f"pr-{self.pr_number}-{self.sha}"
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def aws_image_tag(self) -> str:
|
|
34
|
-
"""Deterministic ECR image tag: pr-{pr_number}-{sha}-ci"""
|
|
35
|
-
return f"pr-{self.pr_number}-{self.sha}-ci"
|
|
36
|
-
|
|
37
|
-
@property
|
|
38
|
-
def ecs_service_name(self) -> str:
|
|
39
|
-
"""Deterministic ECS service name with -service suffix: pr-{pr_number}-{sha}-service"""
|
|
40
|
-
return f"{self.aws_service_name}-service"
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def short_sha(self) -> str:
|
|
44
|
-
"""7-character SHA for display (GitHub standard)"""
|
|
45
|
-
return self.sha[:7]
|
|
46
|
-
|
|
47
|
-
@property
|
|
48
|
-
def is_active(self) -> bool:
|
|
49
|
-
"""Check if this is the currently active show"""
|
|
50
|
-
return self.status == "running"
|
|
51
|
-
|
|
52
|
-
@property
|
|
53
|
-
def is_building(self) -> bool:
|
|
54
|
-
"""Check if environment is currently building"""
|
|
55
|
-
return self.status == "building"
|
|
56
|
-
|
|
57
|
-
@property
|
|
58
|
-
def is_updating(self) -> bool:
|
|
59
|
-
"""Check if environment is currently updating"""
|
|
60
|
-
return self.status == "updating"
|
|
61
|
-
|
|
62
|
-
def needs_update(self, latest_sha: str) -> bool:
|
|
63
|
-
"""Check if environment needs update to latest SHA"""
|
|
64
|
-
return self.sha != latest_sha[:7]
|
|
65
|
-
|
|
66
|
-
def to_circus_labels(self) -> List[str]:
|
|
67
|
-
"""Convert show state to circus tent emoji labels (per-SHA format)"""
|
|
68
|
-
if not self.created_at:
|
|
69
|
-
self.created_at = datetime.utcnow().strftime("%Y-%m-%dT%H-%M")
|
|
70
|
-
|
|
71
|
-
labels = [
|
|
72
|
-
f"🎪 {self.sha} 🚦 {self.status}", # SHA-first status
|
|
73
|
-
f"🎪 🎯 {self.sha}", # Active pointer (no value)
|
|
74
|
-
f"🎪 {self.sha} 📅 {self.created_at}", # SHA-first timestamp
|
|
75
|
-
f"🎪 {self.sha} ⌛ {self.ttl}", # SHA-first TTL
|
|
76
|
-
]
|
|
77
|
-
|
|
78
|
-
if self.ip:
|
|
79
|
-
labels.append(f"🎪 {self.sha} 🌐 {self.ip}:8080")
|
|
80
|
-
|
|
81
|
-
if self.requested_by:
|
|
82
|
-
labels.append(f"🎪 {self.sha} 🤡 {self.requested_by}")
|
|
83
|
-
|
|
84
|
-
return labels
|
|
85
|
-
|
|
86
|
-
@classmethod
|
|
87
|
-
def from_circus_labels(cls, pr_number: int, labels: List[str], sha: str) -> Optional["Show"]:
|
|
88
|
-
"""Create Show from circus tent labels for specific SHA"""
|
|
89
|
-
show_data = {
|
|
90
|
-
"pr_number": pr_number,
|
|
91
|
-
"sha": sha,
|
|
92
|
-
"status": "building", # default
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for label in labels:
|
|
96
|
-
if not label.startswith("🎪 "):
|
|
97
|
-
continue
|
|
98
|
-
|
|
99
|
-
parts = label.split(" ", 3) # Split into 4 parts for per-SHA format
|
|
100
|
-
|
|
101
|
-
if len(parts) == 3: # Old format: 🎪 🎯 sha
|
|
102
|
-
emoji, value = parts[1], parts[2]
|
|
103
|
-
if emoji == "🎯" and value == sha: # Active pointer
|
|
104
|
-
pass # This SHA is active
|
|
105
|
-
elif len(parts) == 4: # SHA-first format: 🎪 sha 🚦 status
|
|
106
|
-
label_sha, emoji, value = parts[1], parts[2], parts[3]
|
|
107
|
-
|
|
108
|
-
if label_sha != sha: # Only process labels for this SHA
|
|
109
|
-
continue
|
|
110
|
-
|
|
111
|
-
if emoji == "🚦": # Status
|
|
112
|
-
show_data["status"] = value
|
|
113
|
-
elif emoji == "📅": # Timestamp
|
|
114
|
-
show_data["created_at"] = value
|
|
115
|
-
elif emoji == "🌐": # IP with port
|
|
116
|
-
show_data["ip"] = value.replace(":8080", "") # Remove port for storage
|
|
117
|
-
elif emoji == "⌛": # TTL
|
|
118
|
-
show_data["ttl"] = value
|
|
119
|
-
elif emoji == "🤡": # User (clown!)
|
|
120
|
-
show_data["requested_by"] = value
|
|
121
|
-
|
|
122
|
-
# Only return Show if we found relevant labels for this SHA
|
|
123
|
-
if any(label.endswith(f" {sha}") for label in labels if "🎯" in label or "🏗️" in label):
|
|
124
|
-
return cls(**show_data)
|
|
125
|
-
|
|
126
|
-
return None
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
class PullRequest:
|
|
130
|
-
"""GitHub PR with its shows parsed from circus labels"""
|
|
131
|
-
|
|
132
|
-
def __init__(self, pr_number: int, labels: List[str]):
|
|
133
|
-
self.pr_number = pr_number
|
|
134
|
-
self.labels = labels
|
|
135
|
-
self._shows = self._parse_shows_from_labels()
|
|
136
|
-
|
|
137
|
-
@property
|
|
138
|
-
def shows(self) -> List[Show]:
|
|
139
|
-
"""All shows found in labels"""
|
|
140
|
-
return self._shows
|
|
141
|
-
|
|
142
|
-
@property
|
|
143
|
-
def current_show(self) -> Optional[Show]:
|
|
144
|
-
"""The currently active show (from 🎯 label)"""
|
|
145
|
-
# Find the SHA that's marked as active (🎯)
|
|
146
|
-
active_sha = None
|
|
147
|
-
for label in self.labels:
|
|
148
|
-
if label.startswith("🎪 🎯 "):
|
|
149
|
-
active_sha = label.split(" ")[2]
|
|
150
|
-
break
|
|
151
|
-
|
|
152
|
-
if not active_sha:
|
|
153
|
-
return None
|
|
154
|
-
|
|
155
|
-
# Find the show with that SHA
|
|
156
|
-
for show in self.shows:
|
|
157
|
-
if show.sha == active_sha:
|
|
158
|
-
return show
|
|
159
|
-
|
|
160
|
-
return None
|
|
161
|
-
|
|
162
|
-
@property
|
|
163
|
-
def building_show(self) -> Optional[Show]:
|
|
164
|
-
"""Show currently being built (from 🏗️ label)"""
|
|
165
|
-
building_sha = None
|
|
166
|
-
for label in self.labels:
|
|
167
|
-
if label.startswith("🎪 🏗️ "):
|
|
168
|
-
building_sha = label.split(" ")[2]
|
|
169
|
-
break
|
|
170
|
-
|
|
171
|
-
if not building_sha:
|
|
172
|
-
return None
|
|
173
|
-
|
|
174
|
-
for show in self.shows:
|
|
175
|
-
if show.sha == building_sha:
|
|
176
|
-
return show
|
|
177
|
-
|
|
178
|
-
return None
|
|
179
|
-
|
|
180
|
-
@property
|
|
181
|
-
def circus_labels(self) -> List[str]:
|
|
182
|
-
"""All circus tent labels"""
|
|
183
|
-
return [label for label in self.labels if label.startswith("🎪 ")]
|
|
184
|
-
|
|
185
|
-
def has_shows(self) -> bool:
|
|
186
|
-
"""Check if PR has any shows"""
|
|
187
|
-
return len(self.shows) > 0
|
|
188
|
-
|
|
189
|
-
def get_show_by_sha(self, sha: str) -> Optional[Show]:
|
|
190
|
-
"""Get specific show by SHA"""
|
|
191
|
-
for show in self.shows:
|
|
192
|
-
if show.sha == sha[:7]:
|
|
193
|
-
return show
|
|
194
|
-
return None
|
|
195
|
-
|
|
196
|
-
def _parse_shows_from_labels(self) -> List[Show]:
|
|
197
|
-
"""Parse all shows from circus labels"""
|
|
198
|
-
shows = []
|
|
199
|
-
|
|
200
|
-
# Find all unique SHAs mentioned in labels
|
|
201
|
-
shas = set()
|
|
202
|
-
for label in self.labels:
|
|
203
|
-
if label.startswith("🎪 🎯 ") or label.startswith("🎪 🏗️ "):
|
|
204
|
-
sha = label.split(" ")[2]
|
|
205
|
-
shas.add(sha)
|
|
206
|
-
|
|
207
|
-
# Create Show object for each SHA
|
|
208
|
-
for sha in shas:
|
|
209
|
-
show = Show.from_circus_labels(self.pr_number, self.labels, sha)
|
|
210
|
-
if show:
|
|
211
|
-
shows.append(show)
|
|
212
|
-
|
|
213
|
-
return shows
|
|
214
|
-
|
|
215
|
-
@classmethod
|
|
216
|
-
def from_id(cls, pr_number: int, github: "GitHubInterface") -> "PullRequest":
|
|
217
|
-
"""Load PR with current labels from GitHub"""
|
|
218
|
-
labels = github.get_labels(pr_number)
|
|
219
|
-
return cls(pr_number, labels)
|
|
220
|
-
|
|
221
|
-
def refresh_labels(self, github: "GitHubInterface") -> None:
|
|
222
|
-
"""Refresh labels from GitHub and reparse shows"""
|
|
223
|
-
self.labels = github.get_labels(self.pr_number)
|
|
224
|
-
self._shows = self._parse_shows_from_labels()
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def parse_ttl_days(ttl_str: str) -> Optional[float]:
|
|
228
|
-
"""Parse TTL string to days"""
|
|
229
|
-
import re
|
|
230
|
-
|
|
231
|
-
if ttl_str == "never":
|
|
232
|
-
return None # Never expire
|
|
233
|
-
|
|
234
|
-
if ttl_str == "close":
|
|
235
|
-
return None # Special handling needed
|
|
236
|
-
|
|
237
|
-
# Parse number + unit: 24h, 7d, 2w, etc.
|
|
238
|
-
match = re.match(r"(\d+(?:\.\d+)?)(h|d|w)", ttl_str.lower())
|
|
239
|
-
if not match:
|
|
240
|
-
return 2.0 # Default 2 days if invalid
|
|
241
|
-
|
|
242
|
-
value = float(match.group(1))
|
|
243
|
-
unit = match.group(2)
|
|
244
|
-
|
|
245
|
-
if unit == "h":
|
|
246
|
-
return value / 24.0 # Hours to days
|
|
247
|
-
elif unit == "d":
|
|
248
|
-
return value # Already in days
|
|
249
|
-
elif unit == "w":
|
|
250
|
-
return value * 7.0 # Weeks to days
|
|
251
|
-
|
|
252
|
-
return 2.0 # Default
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def get_effective_ttl(pr) -> Optional[float]:
|
|
256
|
-
"""Get effective TTL in days for a PR (handles multiple labels, conflicts)"""
|
|
257
|
-
ttl_labels = []
|
|
258
|
-
|
|
259
|
-
# Find all TTL labels for all shows
|
|
260
|
-
for show in pr.shows:
|
|
261
|
-
if show.ttl:
|
|
262
|
-
ttl_days = parse_ttl_days(show.ttl)
|
|
263
|
-
if ttl_days is None: # "never" or "close"
|
|
264
|
-
if show.ttl == "never":
|
|
265
|
-
return None # Never expire takes precedence
|
|
266
|
-
# For "close", continue checking others
|
|
267
|
-
else:
|
|
268
|
-
ttl_labels.append(ttl_days)
|
|
269
|
-
|
|
270
|
-
if not ttl_labels:
|
|
271
|
-
return 2.0 # Default 2 days
|
|
272
|
-
|
|
273
|
-
# Use longest duration if multiple labels
|
|
274
|
-
return max(ttl_labels)
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def short_sha(full_sha: str) -> str:
|
|
278
|
-
"""Truncate SHA to 7 characters for display (GitHub standard)"""
|
|
279
|
-
return full_sha[:7]
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
showtime/__init__.py,sha256=1wse0Q42I1-_VwF1AYaIDYvkqx4eC32omodFrMeStlE,420
|
|
2
|
-
showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
|
|
3
|
-
showtime/cli.py,sha256=gd8OIjdQov7ei8A9A2X8bdreOeWJVhc5FFhAN9u_spQ,62913
|
|
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=xWrje3MS3LpFeilH6jloWy_VgE9Wky_G5VkRCOB0ZNw,32535
|
|
8
|
-
showtime/core/circus.py,sha256=dtYEU1YnmaE9oWhPwrPiMof1GF5tzckvcDrc9iJ9VpQ,9037
|
|
9
|
-
showtime/core/emojis.py,sha256=MHEDuPIdfNiop4zbNLuviz3eY05QiftYSHHCVbkfKhw,2129
|
|
10
|
-
showtime/core/github.py,sha256=HWhM8_Yq4P-AHq0FV3UfrfQHUHXxkhn74vvc_9RguKA,9822
|
|
11
|
-
showtime/core/github_messages.py,sha256=iKd3AS0uvTnCtR-2jPtEq_efuThC8HUzEAMmQt5c3y4,7187
|
|
12
|
-
showtime/core/label_colors.py,sha256=efhbFnz_3nqEnEqmgyF6_hZbxtCu_fmb68BIIUpSsnk,3895
|
|
13
|
-
showtime/data/ecs-task-definition.json,sha256=0ZaE0FZ8IWduXd2RyscMhXeVgxyym6qtjH02CK9mXBI,2235
|
|
14
|
-
superset_showtime-0.2.8.dist-info/METADATA,sha256=fMZkdKW3pgcxx-AXRP5rmTpmOBLEXtVcSaLr2L5A7xg,14635
|
|
15
|
-
superset_showtime-0.2.8.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
-
superset_showtime-0.2.8.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
|
|
17
|
-
superset_showtime-0.2.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|