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.
- superset_showtime-0.6.10/.claude/settings.local.json +70 -0
- superset_showtime-0.6.10/.gitignore +149 -0
- superset_showtime-0.6.10/.pre-commit-config.yaml +27 -0
- superset_showtime-0.6.10/CLAUDE.md +232 -0
- superset_showtime-0.6.10/Makefile +54 -0
- superset_showtime-0.6.10/PKG-INFO +327 -0
- superset_showtime-0.6.10/README.md +261 -0
- superset_showtime-0.6.10/dev-setup.sh +42 -0
- superset_showtime-0.6.10/pypi-push.sh +24 -0
- superset_showtime-0.6.10/pyproject.toml +144 -0
- superset_showtime-0.6.10/requirements-dev.txt +196 -0
- superset_showtime-0.6.10/requirements.txt +63 -0
- superset_showtime-0.6.10/showtime/__init__.py +20 -0
- superset_showtime-0.6.10/showtime/__main__.py +8 -0
- superset_showtime-0.6.10/showtime/cli.py +932 -0
- superset_showtime-0.6.10/showtime/core/__init__.py +1 -0
- superset_showtime-0.6.10/showtime/core/aws.py +847 -0
- superset_showtime-0.6.10/showtime/core/constants.py +10 -0
- superset_showtime-0.6.10/showtime/core/date_utils.py +123 -0
- superset_showtime-0.6.10/showtime/core/emojis.py +77 -0
- superset_showtime-0.6.10/showtime/core/git_validation.py +213 -0
- superset_showtime-0.6.10/showtime/core/github.py +334 -0
- superset_showtime-0.6.10/showtime/core/github_messages.py +242 -0
- superset_showtime-0.6.10/showtime/core/label_colors.py +137 -0
- superset_showtime-0.6.10/showtime/core/pull_request.py +1152 -0
- superset_showtime-0.6.10/showtime/core/service_name.py +104 -0
- superset_showtime-0.6.10/showtime/core/show.py +299 -0
- superset_showtime-0.6.10/showtime/core/sync_state.py +137 -0
- superset_showtime-0.6.10/showtime/data/ecs-task-definition.json +79 -0
- superset_showtime-0.6.10/tests/__init__.py +1 -0
- superset_showtime-0.6.10/tests/unit/__init__.py +1 -0
- superset_showtime-0.6.10/tests/unit/test_aws_service_deletion.py +209 -0
- superset_showtime-0.6.10/tests/unit/test_label_transitions.py +332 -0
- superset_showtime-0.6.10/tests/unit/test_pull_request.py +1059 -0
- superset_showtime-0.6.10/tests/unit/test_sha_specific_logic.py +205 -0
- superset_showtime-0.6.10/tests/unit/test_show.py +326 -0
- superset_showtime-0.6.10/tests/unit/test_ttl.py +214 -0
- superset_showtime-0.6.10/uv.lock +2 -0
- superset_showtime-0.6.10/workflows-reference/showtime-cleanup.yml +50 -0
- 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}')"
|