superset-showtime 0.1.0__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.
Potentially problematic release.
This version of superset-showtime might be problematic. Click here for more details.
- superset_showtime-0.1.0/.claude/settings.local.json +24 -0
- superset_showtime-0.1.0/.github/workflows/showtime-cleanup.yml +40 -0
- superset_showtime-0.1.0/.github/workflows/showtime.yml +185 -0
- superset_showtime-0.1.0/.gitignore +149 -0
- superset_showtime-0.1.0/.pre-commit-config.yaml +19 -0
- superset_showtime-0.1.0/CLAUDE.md +185 -0
- superset_showtime-0.1.0/Makefile +54 -0
- superset_showtime-0.1.0/PKG-INFO +391 -0
- superset_showtime-0.1.0/README.md +326 -0
- superset_showtime-0.1.0/dev-setup.sh +42 -0
- superset_showtime-0.1.0/pypi-push.sh +24 -0
- superset_showtime-0.1.0/pyproject.toml +140 -0
- superset_showtime-0.1.0/requirements-dev.txt +178 -0
- superset_showtime-0.1.0/requirements.txt +63 -0
- superset_showtime-0.1.0/showtime/__init__.py +21 -0
- superset_showtime-0.1.0/showtime/__main__.py +8 -0
- superset_showtime-0.1.0/showtime/cli.py +1361 -0
- superset_showtime-0.1.0/showtime/commands/__init__.py +1 -0
- superset_showtime-0.1.0/showtime/commands/start.py +40 -0
- superset_showtime-0.1.0/showtime/core/__init__.py +1 -0
- superset_showtime-0.1.0/showtime/core/aws.py +758 -0
- superset_showtime-0.1.0/showtime/core/circus.py +285 -0
- superset_showtime-0.1.0/showtime/core/config.py +152 -0
- superset_showtime-0.1.0/showtime/core/emojis.py +86 -0
- superset_showtime-0.1.0/showtime/core/github.py +214 -0
- superset_showtime-0.1.0/showtime/data/ecs-task-definition.json +59 -0
- superset_showtime-0.1.0/tests/__init__.py +1 -0
- superset_showtime-0.1.0/tests/unit/__init__.py +1 -0
- superset_showtime-0.1.0/tests/unit/test_circus.py +157 -0
- superset_showtime-0.1.0/uv.lock +2 -0
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
],
|
|
21
|
+
"deny": [],
|
|
22
|
+
"ask": []
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: 🎪 Showtime Cleanup
|
|
2
|
+
|
|
3
|
+
# Scheduled cleanup of expired environments
|
|
4
|
+
on:
|
|
5
|
+
schedule:
|
|
6
|
+
- cron: '0 */6 * * *' # Every 6 hours
|
|
7
|
+
|
|
8
|
+
# Manual trigger for testing
|
|
9
|
+
workflow_dispatch:
|
|
10
|
+
inputs:
|
|
11
|
+
max_age_hours:
|
|
12
|
+
description: 'Maximum age in hours before cleanup'
|
|
13
|
+
required: false
|
|
14
|
+
default: '48'
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
cleanup-expired:
|
|
18
|
+
name: Clean up expired showtime environments
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
|
|
21
|
+
permissions:
|
|
22
|
+
contents: read
|
|
23
|
+
pull-requests: write
|
|
24
|
+
|
|
25
|
+
steps:
|
|
26
|
+
- name: Install Superset Showtime
|
|
27
|
+
run: |
|
|
28
|
+
pip install superset-showtime
|
|
29
|
+
|
|
30
|
+
- name: Cleanup expired environments
|
|
31
|
+
env:
|
|
32
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
33
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
34
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
35
|
+
AWS_REGION: ${{ vars.AWS_REGION || 'us-west-2' }}
|
|
36
|
+
GITHUB_ORG: ${{ github.repository_owner }}
|
|
37
|
+
GITHUB_REPO: ${{ github.event.repository.name }}
|
|
38
|
+
run: |
|
|
39
|
+
MAX_AGE="${{ github.event.inputs.max_age_hours || '48' }}"
|
|
40
|
+
python -m showtime cleanup --older-than "${MAX_AGE}h"
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
name: 🎪 Superset Showtime Environment Manager
|
|
2
|
+
|
|
3
|
+
# Trigger on circus tent labels
|
|
4
|
+
on:
|
|
5
|
+
pull_request_target:
|
|
6
|
+
types: [labeled, unlabeled, synchronize, closed]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
# Job 1: Check for showtime triggers and extract SHA
|
|
10
|
+
evaluate-triggers:
|
|
11
|
+
name: Evaluate showtime triggers
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
permissions:
|
|
14
|
+
pull-requests: write
|
|
15
|
+
outputs:
|
|
16
|
+
should-build: ${{ steps.eval-triggers.outputs.should-build }}
|
|
17
|
+
should-cleanup: ${{ steps.eval-triggers.outputs.should-cleanup }}
|
|
18
|
+
sha: ${{ steps.get-sha.outputs.sha }}
|
|
19
|
+
env:
|
|
20
|
+
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
|
|
21
|
+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
22
|
+
|
|
23
|
+
steps:
|
|
24
|
+
- name: Check for showtime trigger labels
|
|
25
|
+
id: eval-triggers
|
|
26
|
+
run: |
|
|
27
|
+
if [[ "${{ github.event_name }}" == "pull_request_target" && "${{ contains(github.event.label.name, '🎪 trigger-start') }}" == "true" ]]; then
|
|
28
|
+
echo "should-build=true" >> $GITHUB_OUTPUT
|
|
29
|
+
elif [[ "${{ github.event_name }}" == "synchronize" ]]; then
|
|
30
|
+
echo "should-build=true" >> $GITHUB_OUTPUT
|
|
31
|
+
elif [[ "${{ github.event.action }}" == "closed" ]]; then
|
|
32
|
+
echo "should-build=false" >> $GITHUB_OUTPUT
|
|
33
|
+
echo "should-cleanup=true" >> $GITHUB_OUTPUT
|
|
34
|
+
else
|
|
35
|
+
echo "should-build=false" >> $GITHUB_OUTPUT
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
- name: Get PR SHA
|
|
39
|
+
id: get-sha
|
|
40
|
+
if: steps.eval-triggers.outputs.should-build == 'true'
|
|
41
|
+
uses: actions/github-script@v7
|
|
42
|
+
with:
|
|
43
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
44
|
+
script: |
|
|
45
|
+
const prSha = context.payload.pull_request.head.sha;
|
|
46
|
+
console.log(`PR SHA: ${prSha}`);
|
|
47
|
+
core.setOutput("sha", prSha);
|
|
48
|
+
|
|
49
|
+
# Job 2: Build Docker image (from original ephemeral-env.yml)
|
|
50
|
+
docker-build:
|
|
51
|
+
concurrency:
|
|
52
|
+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }}-build
|
|
53
|
+
cancel-in-progress: true
|
|
54
|
+
needs: evaluate-triggers
|
|
55
|
+
if: needs.evaluate-triggers.outputs.should-build == 'true'
|
|
56
|
+
name: Build ephemeral environment image
|
|
57
|
+
runs-on: ubuntu-24.04
|
|
58
|
+
env:
|
|
59
|
+
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
|
|
60
|
+
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
61
|
+
|
|
62
|
+
steps:
|
|
63
|
+
- name: "Checkout ${{ github.ref }} ( ${{ needs.evaluate-triggers.outputs.sha }} )"
|
|
64
|
+
uses: actions/checkout@v4
|
|
65
|
+
with:
|
|
66
|
+
ref: ${{ needs.evaluate-triggers.outputs.sha }}
|
|
67
|
+
persist-credentials: false
|
|
68
|
+
|
|
69
|
+
- name: Setup Docker Environment
|
|
70
|
+
uses: ./.github/actions/setup-docker
|
|
71
|
+
with:
|
|
72
|
+
dockerhub-user: ${{ secrets.DOCKERHUB_USER }}
|
|
73
|
+
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
|
|
74
|
+
build: "true"
|
|
75
|
+
install-docker-compose: "false"
|
|
76
|
+
|
|
77
|
+
- name: Setup supersetbot
|
|
78
|
+
uses: ./.github/actions/setup-supersetbot/
|
|
79
|
+
|
|
80
|
+
- name: Build ephemeral env image
|
|
81
|
+
env:
|
|
82
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
83
|
+
run: |
|
|
84
|
+
supersetbot docker \
|
|
85
|
+
--push \
|
|
86
|
+
--load \
|
|
87
|
+
--preset ci \
|
|
88
|
+
--platform linux/amd64 \
|
|
89
|
+
--context-ref "${{ needs.evaluate-triggers.outputs.sha }}" \
|
|
90
|
+
--extra-flags "--build-arg INCLUDE_CHROMIUM=false"
|
|
91
|
+
|
|
92
|
+
- name: Configure AWS credentials
|
|
93
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
94
|
+
with:
|
|
95
|
+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
96
|
+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
97
|
+
aws-region: us-west-2
|
|
98
|
+
|
|
99
|
+
- name: Login to Amazon ECR
|
|
100
|
+
id: login-ecr
|
|
101
|
+
uses: aws-actions/amazon-ecr-login@v2
|
|
102
|
+
|
|
103
|
+
- name: Tag and push image to ECR
|
|
104
|
+
env:
|
|
105
|
+
ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
|
|
106
|
+
ECR_REPOSITORY: superset-ci
|
|
107
|
+
IMAGE_TAG: apache/superset:${{ needs.evaluate-triggers.outputs.sha }}-ci
|
|
108
|
+
PR_NUMBER: ${{ github.event.pull_request.number }}
|
|
109
|
+
run: |
|
|
110
|
+
docker tag $IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:pr-$PR_NUMBER-ci
|
|
111
|
+
docker push -a $ECR_REGISTRY/$ECR_REPOSITORY
|
|
112
|
+
|
|
113
|
+
# Job 3: Handle showtime operations (our new logic)
|
|
114
|
+
showtime-handler:
|
|
115
|
+
needs: [evaluate-triggers, docker-build]
|
|
116
|
+
if: needs.evaluate-triggers.outputs.should-build == 'true'
|
|
117
|
+
|
|
118
|
+
concurrency:
|
|
119
|
+
group: showtime-${{ github.event.pull_request.number || github.run_id }}
|
|
120
|
+
cancel-in-progress: true
|
|
121
|
+
|
|
122
|
+
name: Process showtime operations
|
|
123
|
+
runs-on: ubuntu-latest
|
|
124
|
+
|
|
125
|
+
permissions:
|
|
126
|
+
contents: read
|
|
127
|
+
pull-requests: write
|
|
128
|
+
|
|
129
|
+
steps:
|
|
130
|
+
- name: Install Superset Showtime
|
|
131
|
+
run: |
|
|
132
|
+
pip install superset-showtime
|
|
133
|
+
|
|
134
|
+
- name: Process circus triggers
|
|
135
|
+
env:
|
|
136
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
137
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
138
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
139
|
+
AWS_REGION: us-west-2
|
|
140
|
+
GITHUB_ORG: ${{ github.repository_owner }}
|
|
141
|
+
GITHUB_REPO: ${{ github.event.repository.name }}
|
|
142
|
+
GITHUB_ACTOR: ${{ github.actor }}
|
|
143
|
+
run: |
|
|
144
|
+
python -m showtime handle-trigger ${{ github.event.pull_request.number }}
|
|
145
|
+
|
|
146
|
+
- name: Handle synchronize events (new commits)
|
|
147
|
+
if: github.event_name == 'synchronize'
|
|
148
|
+
env:
|
|
149
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
150
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
151
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
152
|
+
AWS_REGION: us-west-2
|
|
153
|
+
GITHUB_ORG: ${{ github.repository_owner }}
|
|
154
|
+
GITHUB_REPO: ${{ github.event.repository.name }}
|
|
155
|
+
GITHUB_ACTOR: ${{ github.actor }}
|
|
156
|
+
run: |
|
|
157
|
+
python -m showtime handle-sync ${{ github.event.pull_request.number }}
|
|
158
|
+
|
|
159
|
+
# Job 4: Immediate cleanup when PR is closed/merged
|
|
160
|
+
pr-cleanup:
|
|
161
|
+
needs: evaluate-triggers
|
|
162
|
+
if: needs.evaluate-triggers.outputs.should-cleanup == 'true'
|
|
163
|
+
name: Clean up closed PR environment
|
|
164
|
+
runs-on: ubuntu-latest
|
|
165
|
+
|
|
166
|
+
permissions:
|
|
167
|
+
contents: read
|
|
168
|
+
pull-requests: write
|
|
169
|
+
|
|
170
|
+
steps:
|
|
171
|
+
- name: Install Superset Showtime
|
|
172
|
+
run: |
|
|
173
|
+
pip install superset-showtime
|
|
174
|
+
|
|
175
|
+
- name: Clean up closed PR
|
|
176
|
+
env:
|
|
177
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
178
|
+
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
|
179
|
+
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
|
180
|
+
AWS_REGION: us-west-2
|
|
181
|
+
GITHUB_ORG: ${{ github.repository_owner }}
|
|
182
|
+
GITHUB_REPO: ${{ github.event.repository.name }}
|
|
183
|
+
GITHUB_ACTOR: ${{ github.actor }}
|
|
184
|
+
run: |
|
|
185
|
+
python -m showtime stop ${{ github.event.pull_request.number }} --force
|
|
@@ -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,19 @@
|
|
|
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
|
|
@@ -0,0 +1,185 @@
|
|
|
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 Python CLI tool for managing Apache Superset ephemeral environments using **circus tent emoji labels** as a state management system on GitHub PRs. The system replaces complex GitHub Actions scripts with a simple CLI that uses GitHub labels for ACID-like state transactions.
|
|
8
|
+
|
|
9
|
+
## Development Commands
|
|
10
|
+
|
|
11
|
+
### Essential Commands
|
|
12
|
+
```bash
|
|
13
|
+
# Development setup
|
|
14
|
+
uv pip install -e ".[dev]" # Install with dev dependencies
|
|
15
|
+
make pre-commit # Setup pre-commit hooks
|
|
16
|
+
make test # Run tests
|
|
17
|
+
make lint # Run ruff + mypy
|
|
18
|
+
|
|
19
|
+
# Testing circus tent functionality
|
|
20
|
+
python -m showtime --help # Shows complete workflow tutorial
|
|
21
|
+
python -m showtime labels # Complete label reference
|
|
22
|
+
python -m showtime list # List all environments (requires GITHUB_TOKEN)
|
|
23
|
+
python -m showtime test-lifecycle 1234 # Full workflow simulation
|
|
24
|
+
|
|
25
|
+
# Test with real GitHub PRs safely
|
|
26
|
+
export GITHUB_TOKEN=xxx
|
|
27
|
+
showtime start 34809 --dry-run-aws --aws-sleep 5 # Mock AWS, real GitHub labels
|
|
28
|
+
showtime status 34809 # Check environment status
|
|
29
|
+
showtime stop 34809 --force # Clean up test labels
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Testing Single Components
|
|
33
|
+
```bash
|
|
34
|
+
pytest tests/unit/test_circus.py # Test label parsing
|
|
35
|
+
pytest tests/unit/test_circus.py::test_show_properties -v # Single test
|
|
36
|
+
python -c "from showtime.core.circus import Show; show = Show(pr_number=1234, sha='abc123f', status='running'); print(show.to_circus_labels())" # Quick circus label test
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Architecture Overview
|
|
40
|
+
|
|
41
|
+
### Core State Management Pattern
|
|
42
|
+
The system uses **GitHub labels as a distributed state machine**:
|
|
43
|
+
- **Trigger labels** (`🎪 trigger-start`) - Commands added by users, processed and removed by CLI
|
|
44
|
+
- **State labels** (`🎪 🚦 abc123f running`) - Per-SHA status managed by CLI
|
|
45
|
+
- **No external database** - All state reconstructed from GitHub labels
|
|
46
|
+
|
|
47
|
+
### Key Classes and Responsibilities
|
|
48
|
+
|
|
49
|
+
#### `Show` (showtime/core/circus.py:16)
|
|
50
|
+
Represents a single ephemeral environment with per-SHA state:
|
|
51
|
+
- **Properties**: `aws_service_name`, `aws_image_tag` for deterministic AWS naming
|
|
52
|
+
- **Methods**: `to_circus_labels()` creates per-SHA format labels
|
|
53
|
+
- **Parsing**: `from_circus_labels()` reconstructs from GitHub labels
|
|
54
|
+
|
|
55
|
+
#### `PullRequest` (showtime/core/circus.py:121)
|
|
56
|
+
Container parsing all shows from a PR's labels:
|
|
57
|
+
- **Properties**: `current_show`, `building_show`, `circus_labels`
|
|
58
|
+
- **State reconstruction**: Parses all environments from labels
|
|
59
|
+
- **Factory method**: `from_id()` loads real GitHub data
|
|
60
|
+
|
|
61
|
+
#### `GitHubInterface` (showtime/core/github.py:23)
|
|
62
|
+
GitHub API operations with emoji label support:
|
|
63
|
+
- **Authentication**: Auto-detects `GITHUB_TOKEN` or `gh auth token`
|
|
64
|
+
- **Label operations**: Handles emoji encoding for GitHub API
|
|
65
|
+
- **Discovery**: `find_prs_with_shows()` searches for circus tent labels
|
|
66
|
+
|
|
67
|
+
#### `AWSInterface` (showtime/core/aws.py:35)
|
|
68
|
+
Replicates current GitHub Actions AWS logic:
|
|
69
|
+
- **Naming**: Uses same deterministic patterns as current GHA
|
|
70
|
+
- **Operations**: `create_environment()`, `delete_environment()` mirror existing workflows
|
|
71
|
+
- **Network config**: Same subnets/security groups as production
|
|
72
|
+
|
|
73
|
+
### Label Format Design
|
|
74
|
+
|
|
75
|
+
#### Per-SHA State Labels
|
|
76
|
+
```bash
|
|
77
|
+
🎪 🚦 {sha} {status} # 🎪 🚦 abc123f running
|
|
78
|
+
🎪 🌐 {sha} {ip-dashes} # 🎪 🌐 abc123f 52-1-2-3
|
|
79
|
+
🎪 📅 {sha} {timestamp} # 🎪 📅 abc123f 2024-01-15T14-30
|
|
80
|
+
🎪 ⌛ {sha} {ttl} # 🎪 ⌛ abc123f 24h
|
|
81
|
+
🎪 🤡 {sha} {username} # 🎪 🤡 abc123f maxime
|
|
82
|
+
🎪 ⚙️ {sha} {config} # 🎪 ⚙️ abc123f alerts,debug
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
#### Pointer Labels (No Value)
|
|
86
|
+
```bash
|
|
87
|
+
🎪 🎯 {sha} # Active environment pointer
|
|
88
|
+
🎪 🏗️ {sha} # Building environment pointer
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### GitHub Actions Integration
|
|
92
|
+
|
|
93
|
+
#### Current Integration Points
|
|
94
|
+
- **`.github/workflows/circus.yml`** - Main workflow replacing `ephemeral-env.yml`
|
|
95
|
+
- **`.github/workflows/circus-cleanup.yml`** - Scheduled cleanup replacing manual processes
|
|
96
|
+
- **Security model**: `pull_request_target` + PyPI install (no PR code execution)
|
|
97
|
+
|
|
98
|
+
#### CLI Commands Called by GHA
|
|
99
|
+
- `showtime handle-trigger {pr-number}` - Process trigger labels
|
|
100
|
+
- `showtime handle-sync {pr-number}` - Handle new commits
|
|
101
|
+
- `showtime cleanup --older-than 48h` - Scheduled cleanup
|
|
102
|
+
|
|
103
|
+
## Important Implementation Details
|
|
104
|
+
|
|
105
|
+
### Authentication Pattern
|
|
106
|
+
```python
|
|
107
|
+
# Priority order for credentials:
|
|
108
|
+
github = GitHubInterface() # Auto-detects GITHUB_TOKEN or gh CLI
|
|
109
|
+
# No config files - environment variables only
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Dry-Run Testing Strategy
|
|
113
|
+
The CLI has comprehensive dry-run support for safe testing:
|
|
114
|
+
```bash
|
|
115
|
+
--dry-run-aws # Skip AWS operations, use mock data
|
|
116
|
+
--dry-run-github # Skip GitHub operations, show what would happen
|
|
117
|
+
--aws-sleep N # Sleep N seconds to simulate AWS timing
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### AWS Resource Naming
|
|
121
|
+
Must maintain compatibility with existing Superset infrastructure:
|
|
122
|
+
- **ECS Service**: `pr-{pr_number}-{sha}` (e.g., `pr-1234-abc123f`)
|
|
123
|
+
- **ECR Image**: `pr-{pr_number}-{sha}-ci`
|
|
124
|
+
- **Network**: Same subnets/security groups as current production setup
|
|
125
|
+
|
|
126
|
+
### State Recovery Pattern
|
|
127
|
+
Since the CLI is stateless, it always reconstructs state from GitHub labels:
|
|
128
|
+
```python
|
|
129
|
+
pr = PullRequest.from_id(pr_number, github) # Reads all labels
|
|
130
|
+
show = pr.current_show # Finds active environment
|
|
131
|
+
# All state comes from parsing circus tent emoji labels
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Critical Development Notes
|
|
135
|
+
|
|
136
|
+
### Label Operations Must Be Atomic
|
|
137
|
+
GitHub label operations are the "database transactions" - handle carefully:
|
|
138
|
+
```python
|
|
139
|
+
# Remove trigger immediately after processing
|
|
140
|
+
github.remove_label(pr_number, trigger_label)
|
|
141
|
+
# Update state labels atomically
|
|
142
|
+
github.remove_circus_labels(pr_number)
|
|
143
|
+
for label in new_labels:
|
|
144
|
+
github.add_label(pr_number, label)
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Per-SHA Format Required
|
|
148
|
+
All new state labels must include SHA for multi-environment support:
|
|
149
|
+
```python
|
|
150
|
+
# Correct: Per-SHA format
|
|
151
|
+
f"🎪 🚦 {sha} running" # Status per environment
|
|
152
|
+
f"🎪 🌐 {sha} {ip}" # IP per environment
|
|
153
|
+
|
|
154
|
+
# Incorrect: Global format (legacy)
|
|
155
|
+
"🎪 🚦 running" # Which environment??
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Testing Against Real PRs
|
|
159
|
+
The CLI can safely test against real Superset PRs:
|
|
160
|
+
```bash
|
|
161
|
+
# Test label management without AWS costs
|
|
162
|
+
showtime start 34809 --dry-run-aws
|
|
163
|
+
# Removes test labels when done
|
|
164
|
+
showtime stop 34809 --force
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Current Implementation Status
|
|
168
|
+
|
|
169
|
+
### ✅ Working (Tested)
|
|
170
|
+
- GitHub API integration with apache/superset
|
|
171
|
+
- Circus tent emoji label parsing and creation
|
|
172
|
+
- CLI commands: `list`, `status`, `start/stop` (with dry-run)
|
|
173
|
+
- Beautiful help system that teaches label workflow
|
|
174
|
+
- Comprehensive dry-run testing framework
|
|
175
|
+
|
|
176
|
+
### 🚧 Partially Implemented
|
|
177
|
+
- AWS operations (class exists, logic written, needs testing)
|
|
178
|
+
- Per-SHA label parsing (designed, needs full implementation)
|
|
179
|
+
- Trigger processing (framework exists, needs completion)
|
|
180
|
+
|
|
181
|
+
### 📋 Not Implemented
|
|
182
|
+
- Rolling update logic for zero-downtime
|
|
183
|
+
- Real AWS integration testing
|
|
184
|
+
- Feature flag configuration via labels
|
|
185
|
+
- GitHub Actions workflow deployment
|
|
@@ -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.circus import parse_circus_labels; print(parse_circus_labels(['🎪 🚦 running', '🎪 🎯 abc123f']))"
|