superset-showtime 0.6.4__py3-none-any.whl → 0.6.7__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 +1 -1
- showtime/cli.py +115 -62
- showtime/core/aws.py +38 -54
- showtime/core/constants.py +10 -0
- showtime/core/date_utils.py +80 -0
- showtime/core/git_validation.py +21 -12
- showtime/core/github.py +19 -0
- showtime/core/github_messages.py +3 -1
- showtime/core/pull_request.py +307 -39
- showtime/core/service_name.py +104 -0
- showtime/core/show.py +30 -17
- showtime/core/sync_state.py +137 -0
- {superset_showtime-0.6.4.dist-info → superset_showtime-0.6.7.dist-info}/METADATA +1 -1
- superset_showtime-0.6.7.dist-info/RECORD +21 -0
- superset_showtime-0.6.4.dist-info/RECORD +0 -17
- {superset_showtime-0.6.4.dist-info → superset_showtime-0.6.7.dist-info}/WHEEL +0 -0
- {superset_showtime-0.6.4.dist-info → superset_showtime-0.6.7.dist-info}/entry_points.txt +0 -0
showtime/core/show.py
CHANGED
|
@@ -8,6 +8,8 @@ from dataclasses import dataclass
|
|
|
8
8
|
from datetime import datetime
|
|
9
9
|
from typing import List, Optional
|
|
10
10
|
|
|
11
|
+
from .constants import DEFAULT_TTL
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
# Import interfaces for singleton access
|
|
13
15
|
# Note: These will be imported when the module loads, creating singletons
|
|
@@ -28,7 +30,7 @@ class Show:
|
|
|
28
30
|
status: str # building, built, deploying, running, updating, failed
|
|
29
31
|
ip: Optional[str] = None # Environment IP address
|
|
30
32
|
created_at: Optional[str] = None # ISO timestamp
|
|
31
|
-
ttl: str =
|
|
33
|
+
ttl: str = DEFAULT_TTL # 24h, 48h, close, etc.
|
|
32
34
|
requested_by: Optional[str] = None # GitHub username
|
|
33
35
|
|
|
34
36
|
@property
|
|
@@ -80,26 +82,38 @@ class Show:
|
|
|
80
82
|
"""Check if environment needs update to latest SHA"""
|
|
81
83
|
return self.sha != latest_sha[:7]
|
|
82
84
|
|
|
85
|
+
@property
|
|
86
|
+
def created_datetime(self) -> Optional[datetime]:
|
|
87
|
+
"""Parse created_at timestamp into datetime object (UTC)"""
|
|
88
|
+
from .date_utils import parse_circus_time
|
|
89
|
+
|
|
90
|
+
if self.created_at is None:
|
|
91
|
+
return None
|
|
92
|
+
return parse_circus_time(self.created_at)
|
|
93
|
+
|
|
83
94
|
def is_expired(self, max_age_hours: int) -> bool:
|
|
84
95
|
"""Check if this environment is expired based on age"""
|
|
85
|
-
|
|
96
|
+
from .date_utils import is_expired
|
|
97
|
+
|
|
98
|
+
if self.created_at is None:
|
|
86
99
|
return False
|
|
100
|
+
return is_expired(self.created_at, max_age_hours)
|
|
87
101
|
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
def age_display(self) -> str:
|
|
103
|
+
"""Get human-readable age of this environment"""
|
|
104
|
+
from .date_utils import age_display
|
|
90
105
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
except (ValueError, AttributeError):
|
|
95
|
-
return False # If we can't parse, assume not expired
|
|
106
|
+
if self.created_at is None:
|
|
107
|
+
return "unknown"
|
|
108
|
+
return age_display(self.created_at)
|
|
96
109
|
|
|
97
110
|
def to_circus_labels(self) -> List[str]:
|
|
98
111
|
"""Convert show state to circus tent emoji labels (per-SHA format)"""
|
|
112
|
+
from .date_utils import format_utc_now
|
|
99
113
|
from .emojis import CIRCUS_PREFIX, MEANING_TO_EMOJI
|
|
100
114
|
|
|
101
115
|
if not self.created_at:
|
|
102
|
-
self.created_at =
|
|
116
|
+
self.created_at = format_utc_now()
|
|
103
117
|
|
|
104
118
|
labels = [
|
|
105
119
|
f"{CIRCUS_PREFIX} {self.sha} {MEANING_TO_EMOJI['status']} {self.status}", # SHA-first status
|
|
@@ -142,21 +156,20 @@ class Show:
|
|
|
142
156
|
# Mock successful deployment for dry-run
|
|
143
157
|
self.ip = "52.1.2.3"
|
|
144
158
|
|
|
145
|
-
def stop(self, dry_run_github: bool = False, dry_run_aws: bool = False) ->
|
|
159
|
+
def stop(self, dry_run_github: bool = False, dry_run_aws: bool = False) -> bool:
|
|
146
160
|
"""Stop this environment (cleanup AWS resources)
|
|
147
161
|
|
|
148
|
-
|
|
149
|
-
|
|
162
|
+
Returns:
|
|
163
|
+
True if successful, False otherwise
|
|
150
164
|
"""
|
|
151
165
|
github, aws = get_interfaces()
|
|
152
166
|
|
|
153
167
|
# Delete AWS resources (pure technical work)
|
|
154
168
|
if not dry_run_aws:
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
raise Exception(f"Failed to delete AWS service: {self.aws_service_name}")
|
|
169
|
+
result = aws.delete_environment(self.aws_service_name, self.pr_number)
|
|
170
|
+
return bool(result)
|
|
158
171
|
|
|
159
|
-
#
|
|
172
|
+
return True # Dry run is always "successful"
|
|
160
173
|
|
|
161
174
|
def _build_docker_image(self) -> None:
|
|
162
175
|
"""Build Docker image for this environment"""
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"""
|
|
2
|
+
🎪 Sync State - Typed state management for sync operations
|
|
3
|
+
|
|
4
|
+
Provides structured state tracking with proper typing for analysis and debugging.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ActionNeeded(Enum):
|
|
13
|
+
"""Actions that sync can take"""
|
|
14
|
+
|
|
15
|
+
NO_ACTION = "no_action"
|
|
16
|
+
CREATE_ENVIRONMENT = "create_environment"
|
|
17
|
+
ROLLING_UPDATE = "rolling_update"
|
|
18
|
+
AUTO_SYNC = "auto_sync"
|
|
19
|
+
DESTROY_ENVIRONMENT = "destroy_environment"
|
|
20
|
+
BLOCKED = "blocked"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class AuthStatus(Enum):
|
|
24
|
+
"""Authorization check results"""
|
|
25
|
+
|
|
26
|
+
AUTHORIZED = "authorized"
|
|
27
|
+
DENIED_INSUFFICIENT_PERMS = "denied_insufficient_perms"
|
|
28
|
+
DENIED_404 = "denied_404"
|
|
29
|
+
ALLOWED_NO_ACTOR = "allowed_no_actor"
|
|
30
|
+
SKIPPED_NOT_ACTIONS = "skipped_not_actions"
|
|
31
|
+
ERROR = "error"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class BlockedReason(Enum):
|
|
35
|
+
"""Why an operation is blocked"""
|
|
36
|
+
|
|
37
|
+
EXISTING_BLOCKED_LABEL = "existing_blocked_label"
|
|
38
|
+
AUTHORIZATION_FAILED = "authorization_failed"
|
|
39
|
+
NOT_BLOCKED = "not_blocked"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class SyncState:
|
|
44
|
+
"""Complete state information for a sync operation"""
|
|
45
|
+
|
|
46
|
+
# Core sync decision
|
|
47
|
+
action_needed: ActionNeeded
|
|
48
|
+
build_needed: bool
|
|
49
|
+
sync_needed: bool
|
|
50
|
+
target_sha: str
|
|
51
|
+
|
|
52
|
+
# Authorization info
|
|
53
|
+
github_actor: str
|
|
54
|
+
is_github_actions: bool
|
|
55
|
+
permission_level: str
|
|
56
|
+
auth_status: AuthStatus
|
|
57
|
+
|
|
58
|
+
# Blocking info
|
|
59
|
+
blocked_reason: BlockedReason = BlockedReason.NOT_BLOCKED
|
|
60
|
+
|
|
61
|
+
# Context info
|
|
62
|
+
trigger_labels: Optional[List[str]] = None
|
|
63
|
+
target_show_status: Optional[str] = None
|
|
64
|
+
has_previous_shows: bool = False
|
|
65
|
+
action_reason: str = ""
|
|
66
|
+
|
|
67
|
+
# Error details (if any)
|
|
68
|
+
auth_error: Optional[str] = None
|
|
69
|
+
|
|
70
|
+
def __post_init__(self) -> None:
|
|
71
|
+
"""Initialize default values"""
|
|
72
|
+
if self.trigger_labels is None:
|
|
73
|
+
self.trigger_labels = []
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def is_blocked(self) -> bool:
|
|
77
|
+
"""Check if operation is blocked"""
|
|
78
|
+
return self.action_needed == ActionNeeded.BLOCKED
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def is_authorized(self) -> bool:
|
|
82
|
+
"""Check if actor is authorized"""
|
|
83
|
+
return self.auth_status == AuthStatus.AUTHORIZED
|
|
84
|
+
|
|
85
|
+
def to_gha_stdout(self, pr_number: int) -> str:
|
|
86
|
+
"""Generate GitHub Actions compatible stdout with k=v pairs"""
|
|
87
|
+
lines = [
|
|
88
|
+
f"build_needed={str(self.build_needed).lower()}",
|
|
89
|
+
f"sync_needed={str(self.sync_needed).lower()}",
|
|
90
|
+
f"pr_number={pr_number}",
|
|
91
|
+
f"target_sha={self.target_sha}",
|
|
92
|
+
f"action_needed={self.action_needed.value}",
|
|
93
|
+
f"github_actor={self.github_actor}",
|
|
94
|
+
f"permission_level={self.permission_level}",
|
|
95
|
+
f"auth_status={self.auth_status.value}",
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
# Add blocking info if relevant
|
|
99
|
+
if self.is_blocked:
|
|
100
|
+
lines.append(f"blocked_reason={self.blocked_reason.value}")
|
|
101
|
+
|
|
102
|
+
# Add context info for debugging
|
|
103
|
+
if self.trigger_labels:
|
|
104
|
+
lines.append(f"trigger_labels={','.join(self.trigger_labels)}")
|
|
105
|
+
|
|
106
|
+
if self.target_show_status:
|
|
107
|
+
lines.append(f"target_show_status={self.target_show_status}")
|
|
108
|
+
|
|
109
|
+
lines.append(f"has_previous_shows={str(self.has_previous_shows).lower()}")
|
|
110
|
+
|
|
111
|
+
if self.action_reason:
|
|
112
|
+
lines.append(f"action_reason={self.action_reason}")
|
|
113
|
+
|
|
114
|
+
# Add error info if present
|
|
115
|
+
if self.auth_error:
|
|
116
|
+
lines.append(f"auth_error={self.auth_error}")
|
|
117
|
+
|
|
118
|
+
return "\n".join(lines)
|
|
119
|
+
|
|
120
|
+
def to_debug_summary(self) -> str:
|
|
121
|
+
"""Generate human-readable debug summary"""
|
|
122
|
+
status = "🚫 BLOCKED" if self.is_blocked else "✅ AUTHORIZED"
|
|
123
|
+
|
|
124
|
+
summary = [
|
|
125
|
+
f"🎪 Sync State Summary: {status}",
|
|
126
|
+
f" Action: {self.action_needed.value} ({self.action_reason})",
|
|
127
|
+
f" Actor: {self.github_actor} ({self.permission_level})",
|
|
128
|
+
f" Auth: {self.auth_status.value}",
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
if self.is_blocked:
|
|
132
|
+
summary.append(f" Blocked: {self.blocked_reason.value}")
|
|
133
|
+
|
|
134
|
+
if self.trigger_labels:
|
|
135
|
+
summary.append(f" Triggers: {', '.join(self.trigger_labels)}")
|
|
136
|
+
|
|
137
|
+
return "\n".join(summary)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: superset-showtime
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.7
|
|
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/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
showtime/__init__.py,sha256=2Ky3eSk7FJq4kHOp0cH41haCJ3X8gZ5WpzxUqKXRnNM,448
|
|
2
|
+
showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
|
|
3
|
+
showtime/cli.py,sha256=uGqtmFHXTZd8vMWuoGZ6BLVWzuY34hMx-GMCkgvqvKk,34369
|
|
4
|
+
showtime/core/__init__.py,sha256=54hbdFNGrzuNMBdraezfjT8Zi6g221pKlJ9mREnKwCw,34
|
|
5
|
+
showtime/core/aws.py,sha256=7ifPmYryeuLR2c7RtGGzge8Ny9H6ZL40mDESiM1_fi8,34065
|
|
6
|
+
showtime/core/constants.py,sha256=ssHiswRorqp2CnsZOebz2zqGsnDlMy9aeTQy8gju9fY,290
|
|
7
|
+
showtime/core/date_utils.py,sha256=iQGQ1DfOwkWbdSTRM7CZdBhiXfpv26khobln5jg1d7k,2112
|
|
8
|
+
showtime/core/emojis.py,sha256=arK0N5Q5FLkvOkci-lacb3WS56LTvY8NjYRqt_lhP9s,2188
|
|
9
|
+
showtime/core/git_validation.py,sha256=2V9BSEjubGG4EHKZjcOUGYDWddNpB9uBZ_EVdU00C60,7203
|
|
10
|
+
showtime/core/github.py,sha256=klUxIzUZRE7iQhG36A0lrtgnt6sTkmUjFazaXGgMlCY,12940
|
|
11
|
+
showtime/core/github_messages.py,sha256=LDYAbVHMEwJaldeJy0ddKe91oRV3RVsUNAXo-RU5oDs,7490
|
|
12
|
+
showtime/core/label_colors.py,sha256=gSe7EIMl4YjWkIgKHUvuaRSwgEB_B-NYQBxFFlF8Z3s,4065
|
|
13
|
+
showtime/core/pull_request.py,sha256=vFlv6pTR3xI5jSODEaQh_af7CUA5kf1StQqQhmzVczI,44964
|
|
14
|
+
showtime/core/service_name.py,sha256=ZgYzlgnMdMqqatCMVarSyzv9Za9njxDSfIvgYd0LUIY,3126
|
|
15
|
+
showtime/core/show.py,sha256=VzjOeUG2ra2tdDIzO1EHkZiEt-kMznRk9O2PQ7VuE4I,10224
|
|
16
|
+
showtime/core/sync_state.py,sha256=oe1lTWWPzfvEAzLBAowdsSJZIdmm5NN-RewzpYTj8zE,4147
|
|
17
|
+
showtime/data/ecs-task-definition.json,sha256=d-NLkIhvr4C6AnwDfDIwUTx-6KFMH9wRkt6pVCbqZY4,2365
|
|
18
|
+
superset_showtime-0.6.7.dist-info/METADATA,sha256=_2odjOOHSF_gh7E__iGf2oa4p7a0XV-Ow3qsgW2DVEw,12052
|
|
19
|
+
superset_showtime-0.6.7.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
20
|
+
superset_showtime-0.6.7.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
|
|
21
|
+
superset_showtime-0.6.7.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
showtime/__init__.py,sha256=O8PG7VEz2VYlezufbPc7zRxajuNxLRaW7XTXnOYSki0,448
|
|
2
|
-
showtime/__main__.py,sha256=EVaDaTX69yIhCzChg99vqvFSCN4ELstEt7Mpb9FMZX8,109
|
|
3
|
-
showtime/cli.py,sha256=TLv9NaqPyewKJi9uCTZKWBijGelqunmsoSo7cyKajV4,31640
|
|
4
|
-
showtime/core/__init__.py,sha256=54hbdFNGrzuNMBdraezfjT8Zi6g221pKlJ9mREnKwCw,34
|
|
5
|
-
showtime/core/aws.py,sha256=uTjJAvEBQMyTccS93WZeNPhfeKQhJgOQQ0BJdnQjvCU,35007
|
|
6
|
-
showtime/core/emojis.py,sha256=arK0N5Q5FLkvOkci-lacb3WS56LTvY8NjYRqt_lhP9s,2188
|
|
7
|
-
showtime/core/git_validation.py,sha256=3dmSGpMDplDAmKWHUyoUEPgt3__8oTuBZxbfuhocT00,6831
|
|
8
|
-
showtime/core/github.py,sha256=mSOqRLy2KMDhWUS37V2gJ-CQdeBpEqunBRKL10v5hxU,12268
|
|
9
|
-
showtime/core/github_messages.py,sha256=MfgwCukrEsWWesMsuL8saciDgP4nS-gijzu8DXr-Alg,7450
|
|
10
|
-
showtime/core/label_colors.py,sha256=gSe7EIMl4YjWkIgKHUvuaRSwgEB_B-NYQBxFFlF8Z3s,4065
|
|
11
|
-
showtime/core/pull_request.py,sha256=DCrTYz3Fu2Oh9FRgYTwncvzX7zy4Wa0BZsBwYa3nTpg,32669
|
|
12
|
-
showtime/core/show.py,sha256=sOgZvGXwdcNDsidF1F_XwPXlSeTb8-Zeqhqb8w1pqAM,9973
|
|
13
|
-
showtime/data/ecs-task-definition.json,sha256=d-NLkIhvr4C6AnwDfDIwUTx-6KFMH9wRkt6pVCbqZY4,2365
|
|
14
|
-
superset_showtime-0.6.4.dist-info/METADATA,sha256=SqFxH-mmg3jpLnOp2Gkl2AHJjewmga-nNtSe4HHk8bw,12052
|
|
15
|
-
superset_showtime-0.6.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
-
superset_showtime-0.6.4.dist-info/entry_points.txt,sha256=rDW7oZ57mqyBUS4N_3_R7bZNGVHB-104jwmY-hHC_ck,85
|
|
17
|
-
superset_showtime-0.6.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|