superset-showtime 0.6.10__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.
Files changed (40) hide show
  1. superset_showtime-0.6.10/.claude/settings.local.json +70 -0
  2. superset_showtime-0.6.10/.gitignore +149 -0
  3. superset_showtime-0.6.10/.pre-commit-config.yaml +27 -0
  4. superset_showtime-0.6.10/CLAUDE.md +232 -0
  5. superset_showtime-0.6.10/Makefile +54 -0
  6. superset_showtime-0.6.10/PKG-INFO +327 -0
  7. superset_showtime-0.6.10/README.md +261 -0
  8. superset_showtime-0.6.10/dev-setup.sh +42 -0
  9. superset_showtime-0.6.10/pypi-push.sh +24 -0
  10. superset_showtime-0.6.10/pyproject.toml +144 -0
  11. superset_showtime-0.6.10/requirements-dev.txt +196 -0
  12. superset_showtime-0.6.10/requirements.txt +63 -0
  13. superset_showtime-0.6.10/showtime/__init__.py +20 -0
  14. superset_showtime-0.6.10/showtime/__main__.py +8 -0
  15. superset_showtime-0.6.10/showtime/cli.py +932 -0
  16. superset_showtime-0.6.10/showtime/core/__init__.py +1 -0
  17. superset_showtime-0.6.10/showtime/core/aws.py +847 -0
  18. superset_showtime-0.6.10/showtime/core/constants.py +10 -0
  19. superset_showtime-0.6.10/showtime/core/date_utils.py +123 -0
  20. superset_showtime-0.6.10/showtime/core/emojis.py +77 -0
  21. superset_showtime-0.6.10/showtime/core/git_validation.py +213 -0
  22. superset_showtime-0.6.10/showtime/core/github.py +334 -0
  23. superset_showtime-0.6.10/showtime/core/github_messages.py +242 -0
  24. superset_showtime-0.6.10/showtime/core/label_colors.py +137 -0
  25. superset_showtime-0.6.10/showtime/core/pull_request.py +1152 -0
  26. superset_showtime-0.6.10/showtime/core/service_name.py +104 -0
  27. superset_showtime-0.6.10/showtime/core/show.py +299 -0
  28. superset_showtime-0.6.10/showtime/core/sync_state.py +137 -0
  29. superset_showtime-0.6.10/showtime/data/ecs-task-definition.json +79 -0
  30. superset_showtime-0.6.10/tests/__init__.py +1 -0
  31. superset_showtime-0.6.10/tests/unit/__init__.py +1 -0
  32. superset_showtime-0.6.10/tests/unit/test_aws_service_deletion.py +209 -0
  33. superset_showtime-0.6.10/tests/unit/test_label_transitions.py +332 -0
  34. superset_showtime-0.6.10/tests/unit/test_pull_request.py +1059 -0
  35. superset_showtime-0.6.10/tests/unit/test_sha_specific_logic.py +205 -0
  36. superset_showtime-0.6.10/tests/unit/test_show.py +326 -0
  37. superset_showtime-0.6.10/tests/unit/test_ttl.py +214 -0
  38. superset_showtime-0.6.10/uv.lock +2 -0
  39. superset_showtime-0.6.10/workflows-reference/showtime-cleanup.yml +50 -0
  40. superset_showtime-0.6.10/workflows-reference/showtime-trigger.yml +172 -0
@@ -0,0 +1,70 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Read(//Users/max/code/superset/.github/workflows/**)",
5
+ "Bash(showtime stop:*)",
6
+ "Bash(python:*)",
7
+ "Bash(uv pip install:*)",
8
+ "Bash(aws configure:*)",
9
+ "Bash(env)",
10
+ "Bash(unset:*)",
11
+ "Bash(aws sts:*)",
12
+ "WebSearch",
13
+ "Bash(git add:*)",
14
+ "Bash(git commit:*)",
15
+ "Bash(ruff check:*)",
16
+ "Bash(pytest:*)",
17
+ "Bash(make test:*)",
18
+ "Bash(uv pip compile:*)",
19
+ "Bash(chmod:*)",
20
+ "Read(//Users/max/code/**)",
21
+ "Bash(aws efs create-file-system:*)",
22
+ "Bash(export AWS_PROFILE=\"\")",
23
+ "Bash(AWS_PROFILE=\"\" aws efs create-file-system --tags Key=Name,Value=superset-examples Key=Purpose,Value=shared-examples-duckdb --performance-mode generalPurpose)",
24
+ "Bash(env -u AWS_PROFILE aws efs create-file-system --tags Key=Name,Value=superset-examples Key=Purpose,Value=shared-examples-duckdb --performance-mode generalPurpose)",
25
+ "Bash(env -u AWS_PROFILE aws efs create-file-system --performance-mode generalPurpose)",
26
+ "Bash(env -u AWS_PROFILE aws efs describe-file-systems --query 'FileSystems[?Tags[?Key==`Name` && Value==`superset-examples`]]')",
27
+ "Bash(env -u AWS_PROFILE aws efs describe-file-systems)",
28
+ "Bash(env:*)",
29
+ "Bash(grep:*)",
30
+ "Bash(git grep:*)",
31
+ "Bash(git push:*)",
32
+ "Bash(make lint:*)",
33
+ "Bash(mypy:*)",
34
+ "Bash(sed:*)",
35
+ "Bash(git pull:*)",
36
+ "Bash(git rebase:*)",
37
+ "Bash(git fetch:*)",
38
+ "Read(//Users/max/.claudette/worktrees/showtime/**)",
39
+ "Bash(rg:*)",
40
+ "Bash(find:*)",
41
+ "Bash(make:*)",
42
+ "Bash(showtime status:*)",
43
+ "Bash(showtime sync:*)",
44
+ "Bash(AWS_PROFILE=\"\" showtime sync 34831 --dry-run-aws --dry-run-github)",
45
+ "Bash(git stash:*)",
46
+ "Bash(showtime list:*)",
47
+ "Bash(showtime git-check)",
48
+ "Read(//^def aws_cleanup/,/**)",
49
+ "WebFetch(domain:github.com)",
50
+ "Bash(showtime labels:*)",
51
+ "Read(//Users/max/**)",
52
+ "Bash(showtime cleanup:*)",
53
+ "Bash(gh pr view:*)",
54
+ "Bash(for pr in 34842 35033 35152)",
55
+ "Bash(do echo \"PR $pr:\")",
56
+ "Bash(done)",
57
+ "Bash(cat:*)",
58
+ "Bash(docker pull:*)",
59
+ "Bash(docker run:*)",
60
+ "Bash(docker inspect:*)"
61
+ ],
62
+ "deny": [],
63
+ "ask": [],
64
+ "additionalDirectories": [
65
+ "/private/tmp",
66
+ "/Users/max/code/superset",
67
+ "/Users/max/.claudette/worktrees/showtime_gha/.github/workflows"
68
+ ]
69
+ }
70
+ }
@@ -0,0 +1,149 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ *.manifest
31
+ *.spec
32
+
33
+ # Installer logs
34
+ pip-log.txt
35
+ pip-delete-this-directory.txt
36
+
37
+ # Unit test / coverage reports
38
+ htmlcov/
39
+ .tox/
40
+ .nox/
41
+ .coverage
42
+ .coverage.*
43
+ .cache
44
+ nosetests.xml
45
+ coverage.xml
46
+ *.cover
47
+ *.py,cover
48
+ .hypothesis/
49
+ .pytest_cache/
50
+ cover/
51
+
52
+ # Translations
53
+ *.mo
54
+ *.pot
55
+
56
+ # Django stuff:
57
+ *.log
58
+ local_settings.py
59
+ db.sqlite3
60
+ db.sqlite3-journal
61
+
62
+ # Flask stuff:
63
+ instance/
64
+ .webassets-cache
65
+
66
+ # Scrapy stuff:
67
+ .scrapy
68
+
69
+ # Sphinx documentation
70
+ docs/_build/
71
+
72
+ # PyBuilder
73
+ .pybuilder/
74
+ target/
75
+
76
+ # Jupyter Notebook
77
+ .ipynb_checkpoints
78
+
79
+ # IPython
80
+ profile_default/
81
+ ipython_config.py
82
+
83
+ # pyenv
84
+ .python-version
85
+
86
+ # pipenv
87
+ Pipfile.lock
88
+
89
+ # poetry
90
+ poetry.lock
91
+
92
+ # pdm
93
+ .pdm.toml
94
+
95
+ # PEP 582
96
+ __pypackages__/
97
+
98
+ # Celery stuff
99
+ celerybeat-schedule
100
+ celerybeat.pid
101
+
102
+ # SageMath parsed files
103
+ *.sage.py
104
+
105
+ # Environments
106
+ .env
107
+ .venv
108
+ env/
109
+ venv/
110
+ ENV/
111
+ env.bak/
112
+ venv.bak/
113
+
114
+ # Spyder project settings
115
+ .spyderproject
116
+ .spyproject
117
+
118
+ # Rope project settings
119
+ .ropeproject
120
+
121
+ # mkdocs documentation
122
+ /site
123
+
124
+ # mypy
125
+ .mypy_cache/
126
+ .dmypy.json
127
+ dmypy.json
128
+
129
+ # Pyre type checker
130
+ .pyre/
131
+
132
+ # pytype static type analyzer
133
+ .pytype/
134
+
135
+ # Cython debug symbols
136
+ cython_debug/
137
+
138
+ # PyCharm
139
+ .idea/
140
+
141
+ # VS Code
142
+ .vscode/
143
+
144
+ # macOS
145
+ .DS_Store
146
+
147
+ # Showtime specific
148
+ .showtime/
149
+ config.yml
@@ -0,0 +1,27 @@
1
+ repos:
2
+ - repo: https://github.com/pre-commit/pre-commit-hooks
3
+ rev: v4.5.0
4
+ hooks:
5
+ - id: trailing-whitespace
6
+ - id: end-of-file-fixer
7
+ - id: check-yaml
8
+ - id: check-added-large-files
9
+ - id: check-merge-conflict
10
+ - id: check-toml
11
+ - id: debug-statements
12
+ - id: mixed-line-ending
13
+
14
+ - repo: https://github.com/astral-sh/ruff-pre-commit
15
+ rev: v0.1.9
16
+ hooks:
17
+ - id: ruff
18
+ args: [--fix]
19
+ - id: ruff-format
20
+
21
+ # TODO: Re-enable after fixing boto3-stubs type conflicts
22
+ # - repo: https://github.com/pre-commit/mirrors-mypy
23
+ # rev: v1.8.0
24
+ # hooks:
25
+ # - id: mypy
26
+ # files: ^showtime/
27
+ # additional_dependencies: [types-requests, "boto3-stubs[ecs,ecr,ec2]"]
@@ -0,0 +1,232 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Project Overview
6
+
7
+ Superset Showtime is a CLI tool for managing Apache Superset ephemeral environments using "circus tent emoji labels" as a visual state management system on GitHub PRs. The tool integrates with GitHub Actions to provide automated environment provisioning on AWS ECS/ECR.
8
+
9
+ ## Development Commands
10
+
11
+ **Package Management:**
12
+ ```bash
13
+ # Install for development (preferred)
14
+ uv pip install -e ".[dev]"
15
+
16
+ # Traditional pip installation
17
+ pip install -e ".[dev]"
18
+ ```
19
+
20
+ **Code Quality:**
21
+ ```bash
22
+ make lint # Run ruff and mypy checks
23
+ make format # Auto-format with ruff
24
+ make pre-commit # Install pre-commit hooks
25
+ make pre-commit-run # Run all pre-commit hooks
26
+ ```
27
+
28
+ **Testing:**
29
+ ```bash
30
+ make test # Run pytest
31
+ make test-cov # Run tests with coverage report
32
+ pytest tests/unit/test_circus.py # Run specific test file
33
+ ```
34
+
35
+ **Build and Distribution:**
36
+ ```bash
37
+ make build # Build package with uv
38
+ make publish # Publish to PyPI (use with caution)
39
+ make clean # Clean build artifacts
40
+ ```
41
+
42
+ **Quick Testing:**
43
+ ```bash
44
+ make circus # Test circus emoji parsing logic
45
+ ```
46
+
47
+ ## Core Architecture
48
+
49
+ ### Main Components
50
+
51
+ **CLI Layer (`showtime/cli.py`):**
52
+ - Typer-based CLI with rich output formatting
53
+ - Commands: `sync`, `start`, `stop`, `status`, `list`, `labels`, `cleanup`
54
+ - Primary entry point for GitHub Actions and manual usage
55
+
56
+ **Core Business Logic (`showtime/core/`):**
57
+
58
+ 1. **PullRequest (`pull_request.py`)** - Main orchestrator
59
+ - Manages PR-level state and atomic transactions
60
+ - Handles trigger processing and environment lifecycle
61
+ - Coordinates between GitHub labels and AWS resources
62
+ - Implements sync logic for automatic deployments
63
+
64
+ 2. **Show (`show.py`)** - Individual environment representation
65
+ - Represents a single ephemeral environment
66
+ - Manages Docker builds and AWS deployments
67
+ - Handles state transitions (building → running → failed)
68
+
69
+ 3. **GitHubInterface (`github.py`)** - GitHub API client
70
+ - Label management and PR data fetching
71
+ - Circus tent emoji label parsing and creation
72
+ - Token detection from environment or gh CLI
73
+
74
+ 4. **AWSInterface (`aws.py`)** - AWS operations
75
+ - ECS service deployment and management
76
+ - ECR image management
77
+ - Network configuration and service discovery
78
+
79
+ ### State Management Pattern
80
+
81
+ The system uses GitHub labels as a distributed state machine:
82
+
83
+ **Trigger Labels (User Actions):**
84
+ - `🎪 ⚡ showtime-trigger-start` - Create environment
85
+ - `🎪 🛑 showtime-trigger-stop` - Destroy environment
86
+ - `🎪 🧊 showtime-freeze` - Prevent auto-sync
87
+ - `🎪 🔒 showtime-blocked` - Block ALL operations (maintenance mode)
88
+
89
+ **State Labels (System Managed):**
90
+ - `🎪 {sha} 🚦 {status}` - Environment status
91
+ - `🎪 🎯 {sha}` - Active environment pointer
92
+ - `🎪 🏗️ {sha}` - Building environment pointer
93
+ - `🎪 {sha} 🌐 {ip}` - Environment URL
94
+ - `🎪 {sha} 📅 {timestamp}` - Creation timestamp
95
+
96
+ ### Atomic Transaction Model
97
+
98
+ The `PullRequest.sync()` method implements an atomic claim pattern:
99
+ 1. **Claim**: Atomically remove trigger labels and set building state
100
+ 2. **Build**: Docker build with deterministic tags (`pr-{number}-{sha}-ci`)
101
+ 3. **Deploy**: AWS ECS service deployment with blue-green updates
102
+ 4. **Validate**: Health checks and state synchronization
103
+
104
+ ## Testing Approach
105
+
106
+ **Unit Tests:** Focus on circus label parsing and business logic
107
+ **Integration Tests:** Test with `--dry-run-aws --dry-run-docker` flags
108
+ **Manual Testing:** Use CLI commands with dry-run modes
109
+
110
+ ## Key Design Principles
111
+
112
+ 1. **Deterministic Naming:** All AWS resources use `pr-{number}-{sha}` pattern
113
+ 2. **Idempotent Operations:** Safe to retry any operation
114
+ 3. **Visual State Management:** GitHub labels provide immediate status visibility
115
+ 4. **Zero-Downtime Updates:** Blue-green deployments with automatic traffic switching
116
+ 5. **Fail-Safe Defaults:** Conservative cleanup and error handling
117
+
118
+ ## Environment Variables
119
+
120
+ **Required:**
121
+ - `GITHUB_TOKEN` - GitHub API access
122
+ - `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` - AWS credentials
123
+
124
+ **Optional:**
125
+ - `AWS_REGION` - Default: us-west-2
126
+ - `ECS_CLUSTER` - Default: superset-ci
127
+ - `ECR_REPOSITORY` - Default: superset-ci
128
+ - `GITHUB_ORG` - Default: apache
129
+ - `GITHUB_REPO` - Default: superset
130
+
131
+ **Superset Configuration (via ECS task definition):**
132
+ - `SERVER_WORKER_AMOUNT` - Gunicorn worker processes (default: 1)
133
+ - `SERVER_THREADS_AMOUNT` - Threads per worker (default: 20)
134
+
135
+ See `showtime/data/ecs-task-definition.json` for complete container environment configuration.
136
+
137
+ ## GitHub Actions Integration
138
+
139
+ The tool is designed to be called from GitHub Actions workflows:
140
+ ```yaml
141
+ - name: Install Superset Showtime
142
+ run: pip install superset-showtime
143
+
144
+ - name: Sync PR state
145
+ run: showtime sync ${{ github.event.number }}
146
+ ```
147
+
148
+ Primary workflow file: `workflows-reference/showtime-trigger.yml`
149
+
150
+ ## Common Development Patterns
151
+
152
+ **Testing without AWS costs:**
153
+ ```bash
154
+ showtime sync 1234 --dry-run-aws --dry-run-docker
155
+ ```
156
+
157
+ **Debugging specific PR:**
158
+ ```bash
159
+ showtime status 1234 --verbose
160
+ showtime list --status running
161
+ ```
162
+
163
+ **Manual environment management:**
164
+ ```bash
165
+ showtime start 1234 --sha abc123f
166
+ showtime stop 1234 --force
167
+ ```
168
+
169
+ ## Race Condition Handling
170
+
171
+ ### Problem Description
172
+
173
+ Double triggers can create race conditions in two scenarios:
174
+
175
+ 1. **Same SHA conflicts**: User pushes commit abc123f twice, creating 2 workflows for identical SHA
176
+ 2. **Stale locks**: Jobs crash or get killed, leaving environments stuck in "building/deploying" state indefinitely
177
+
178
+ ### Current Atomic Claim Mechanism
179
+
180
+ The `PullRequest._atomic_claim()` method handles basic conflicts by:
181
+ 1. Checking if target SHA is already in progress states (`building`, `built`, `deploying`)
182
+ 2. Removing trigger labels atomically
183
+ 3. Setting building state immediately
184
+
185
+ **Limitations**:
186
+ - No distinction between valid locks and stale locks (>1 hour old)
187
+ - `refresh_labels()` is expensive (~500ms) but called on every claim attempt
188
+ - Crashed jobs can leave permanent locks that block future deployments
189
+
190
+ ### Proposed Smart Lock Detection Strategy
191
+
192
+ **Two-phase approach optimizing for the common case**:
193
+
194
+ #### Phase 1: Fast Path (95% of calls, ~5ms)
195
+ ```python
196
+ def can_start_job(self, target_sha: str, action: str, use_cached: bool = True) -> tuple[bool, str]:
197
+ """Fast check using cached self.labels"""
198
+ # Check cached labels for basic conflicts
199
+ # Returns (can_start, reason)
200
+ ```
201
+
202
+ #### Phase 2: Recovery Path (5% of calls, ~500ms)
203
+ ```python
204
+ def double_check_and_cleanup_stale_locks(self, target_sha: str, stale_hours: int = 1, dry_run: bool = False) -> bool:
205
+ """Expensive: refresh labels, detect stale locks (>1h), clean them up"""
206
+ # Only called when fast path detects potential conflict
207
+ # Refreshes labels, checks timestamps, cleans stale AWS resources + GitHub labels
208
+ ```
209
+
210
+ #### Enhanced Atomic Claim Logic
211
+ ```python
212
+ def _atomic_claim(self, target_sha: str, action: str, dry_run: bool = False) -> bool:
213
+ # 1. Fast check with cached labels
214
+ can_start, reason = self.can_start_job(target_sha, action, use_cached=True)
215
+
216
+ if not can_start:
217
+ # 2. Expensive double-check and cleanup
218
+ can_start = self.double_check_and_cleanup_stale_locks(target_sha, stale_hours=1, dry_run=dry_run)
219
+
220
+ # 3. Continue with existing trigger removal + building setup
221
+ ```
222
+
223
+ ### Key Benefits
224
+
225
+ - **Performance**: 95% fast path using cached labels (~5ms vs ~500ms)
226
+ - **Reliability**: Automatic recovery from stale locks (crashed/killed jobs)
227
+ - **Clarity**: Clear distinction between valid conflicts and recoverable states
228
+ - **Safety**: Only cleans locks older than configurable threshold (default: 1 hour)
229
+
230
+ ### Implementation Notes
231
+
232
+ This enhancement can be implemented when race conditions become problematic in practice. The current trigger removal mechanism already handles most same-SHA conflicts effectively due to the speed of GitHub label operations.
@@ -0,0 +1,54 @@
1
+ .PHONY: help install install-dev test test-cov lint format clean pre-commit
2
+
3
+ help: ## Show this help message
4
+ @echo 'Usage: make <target>'
5
+ @echo ''
6
+ @echo 'Available targets:'
7
+ @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-15s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
8
+
9
+ install: ## Install package dependencies
10
+ uv pip install -e .
11
+
12
+ install-dev: ## Install package with development dependencies
13
+ uv pip install -e ".[dev]"
14
+
15
+ test: ## Run tests
16
+ pytest
17
+
18
+ test-cov: ## Run tests with coverage
19
+ pytest --cov=showtime --cov-report=term-missing --cov-report=html
20
+
21
+ lint: ## Run linting
22
+ ruff check .
23
+ mypy showtime
24
+
25
+ format: ## Format code
26
+ ruff format .
27
+ ruff check --fix .
28
+
29
+ clean: ## Clean up build artifacts
30
+ rm -rf build/
31
+ rm -rf dist/
32
+ rm -rf *.egg-info/
33
+ rm -rf htmlcov/
34
+ rm -rf .coverage
35
+ rm -rf .pytest_cache/
36
+ rm -rf .mypy_cache/
37
+ find . -type d -name __pycache__ -exec rm -rf {} +
38
+ find . -type f -name "*.pyc" -delete
39
+
40
+ pre-commit: ## Install pre-commit hooks
41
+ pre-commit install
42
+
43
+ pre-commit-run: ## Run pre-commit hooks on all files
44
+ pre-commit run --all-files
45
+
46
+ build: ## Build package
47
+ uv build
48
+
49
+ publish: ## Publish to PyPI (use with caution)
50
+ uv build
51
+ uvx twine upload dist/*
52
+
53
+ circus: ## Quick test of circus emoji parsing
54
+ python -c "from showtime.core.show import Show; labels=['🎪 abc123f 🚦 running', '🎪 🎯 abc123f']; show=Show.from_circus_labels(1234, labels, 'abc123f'); print(f'Status: {show.status}, SHA: {show.sha}')"