Compare commits

...

25 Commits

Author SHA1 Message Date
Maxime Beauchemin
fe642274f6 fix: only block synchronize events for unauthorized users
Previously the logic was backwards - it was blocking synchronize events
for AUTHORIZED maintainers and allowing them for unauthorized users.

Now:
- Authorized maintainers (write/admin) can push commits → automatic updates
- Unauthorized users pushing to PRs with showtime environments → blocked

This allows trusted maintainers like @mistercrunch to push changes and
get automatic showtime environment updates without manual re-triggering.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-03 14:16:48 -07:00
Maxime Beauchemin
f9d157017e feat: Add audit logging for maintainer deploy triggers
Implements @dpgaspar's suggestion from PR #34833 to add audit trail
when maintainers trigger Showtime deployments. The workflow now logs
which maintainer triggered the deploy and for which PR number.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-28 22:14:25 -07:00
Maxime Beauchemin
1bae0569ca fix: Remove unsupported pattern property from workflow_dispatch input
GitHub Actions workflow_dispatch inputs don't support the 'pattern'
property for input validation. Removed to fix workflow validation errors.

SHA validation will be handled by the showtime CLI instead.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 13:44:05 -07:00
Maxime Beauchemin
7fbea466c9 feat: Add comprehensive security controls to showtime-trigger workflow
- Add maintainer authorization check to prevent unauthorized workflow execution
- Validate SHA input format to prevent injection attacks
- Add 90-minute timeout protection against runaway jobs
- Implement automatic blocking for PR synchronize events when Showtime is active
- Add unlabeled trigger support for proper label removal handling
- Preserve local customizations (install-docker-compose: false, upgrade flag)

Security improvements protect against arbitrary code execution while maintaining
workflow_dispatch convenience for authorized maintainers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-26 10:30:37 -07:00
Maxime Beauchemin
2665c52c21 cleanup: Remove one-time push trigger from showtime-cleanup workflow
The push trigger was only needed for initial workflow registration.
Now that it exists, revert to schedule-only triggers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:39:29 -07:00
Maxime Beauchemin
05bddd6bc3 fix: Remove invalid pattern properties from showtime workflows
- Remove unsupported `pattern` property from workflow_dispatch inputs
- Add one-time push trigger to showtime-cleanup.yml for workflow registration
- Runtime validation still handled in bash steps

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:38:05 -07:00
Maxime Beauchemin
1168475053 remove dummy commit 2025-08-25 13:23:22 -07:00
Maxime Beauchemin
f9b4665525 --upgrade 2025-08-25 13:23:22 -07:00
Maxime Beauchemin
0c9f60635c showtime version 2025-08-25 13:23:22 -07:00
Maxime Beauchemin
60c3b2971d fix: Checkout repository before using local actions
Move checkout step before setup-docker to fix 'Can't find action.yml' error. Local actions require the repository to be checked out first.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
6fe0655e72 feat: Import updated showtime workflows from development session
Replace workflows with improved versions that properly handle showtime CLI output format and fix conditional step execution. Key fixes:
- Match sync_needed output from showtime CLI
- Use target_sha for proper SHA handling
- Remove invalid pattern properties
- Cleaner workflow structure

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
d310dfdc79 fix: Match workflow conditions with actual showtime CLI output
Update output extraction to use 'sync_needed' and 'target_sha' to match what showtime CLI actually returns. This fixes the issue where sync steps were skipped despite sync_needed=true.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
e394f98687 perf: Skip Docker Compose installation in showtime workflow
Add install-docker-compose: false to setup-docker action since showtime workflows don't need Docker Compose. This saves 1-2 minutes by skipping unnecessary apt operations.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
180d5b70ce fix: Fetch PR SHA via GitHub API for workflow_dispatch
For manual workflow_dispatch with PR number, fetch the latest SHA for that specific PR via GitHub API instead of using the current branch SHA. This ensures we build and deploy the actual PR code, not the workflow branch code.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
6ca977ddd5 fix: Use github.sha fallback for workflow_dispatch SHA resolution
When no SHA is provided in workflow_dispatch and there's no PR context, fallback to github.sha (current commit). This fixes the 'No SHA available' error for manual triggers.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
e2a9e28313 perf: Optimize workflow step order for efficiency
Reorder steps to check what's needed first, then only set up Docker environment if build is actually required. This avoids expensive Docker setup when only label changes occur.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
390da591b1 fix: Add SHA handling for push events in showtime workflow
Use github.sha for push events since they don't have pull_request context. This fixes the 'No SHA available' error when the workflow runs on push.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
acf7b61200 perf: Use shallow clone for faster checkout in showtime workflow
Add fetch-depth: 1 to speed up repository checkout when accessing local actions. Only the latest commit is needed for workflow execution.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
e19841fc46 fix: Add checkout step before using local actions
The workflow needs to checkout the repository before using local actions like setup-docker. This resolves the 'Can't find action.yml' error.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
71497054a8 showtime version 2025-08-25 13:23:22 -07:00
Maxime Beauchemin
9fd5815803 fix: Remove pattern property from workflow_dispatch SHA input
GitHub Actions workflow_dispatch inputs don't support the pattern property. SHA validation is now handled in the workflow logic instead.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
fefc2023a9 fix: Handle push events in showtime-trigger workflow
Skip push events in the workflow logic since they're only used for workflow registration. This prevents errors when the workflow runs on push but has no PR context.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
d521f61747 chore: dummy commit to register showtime workflows
This temporary commit will trigger the push event and register the showtime workflows with GitHub Actions, making them discoverable via gh workflow run.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
9f246ba559 fix: Remove invalid pattern property from showtime-cleanup.yml workflow_dispatch input
The pattern property is not supported in workflow_dispatch inputs, causing GitHub Actions validation to fail. Input validation is now handled in the workflow logic instead.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
Maxime Beauchemin
91d0cc5805 feat: Integrate Superset Showtime GitHub Actions workflows
Replaces legacy ephemeral environment system with the new Superset Showtime tool (https://github.com/mistercrunch/superset-showtime), providing automated PR environment management using Docker containers deployed to AWS ECS.

## Key Changes

### New Showtime Workflows
- `showtime-trigger.yml`: Handles all PR events (labeled, synchronize, closed) with intelligent build detection
- `showtime-cleanup.yml`: Scheduled cleanup every 6 hours with manual trigger support

### Legacy Workflow Deprecation
- Added deprecation notices to `ephemeral-env.yml` and `ephemeral-env-pr-close.yml`
- Clear migration guidance from "testenv-up" to "🎪 trigger-start" labels
- Both systems coexist during transition period

### Architecture Improvements
- **Simplified Logic**: GitHub Actions just triggers, showtime CLI handles all decisions
- **Smart Building**: Only builds Docker images when code changes, not label changes
- **DuckDB Support**: Configured with `LOAD_EXAMPLES_DUCKDB=true` build arg
- **Environment Variables**: Requires `SUPERSET__SQLALCHEMY_EXAMPLES_URI` in ECS (showtime CLI responsibility)

## Testing

Simple 3-step test on PR 34831:
1. Set label: `gh pr edit 34831 --add-label "🎪 trigger-start"`
2. Run command: `gh workflow run showtime-trigger.yml --ref showtime_gha --field pr_number=34831`
3. Expected outcome: Environment accessible with DuckDB examples

## Benefits
- **Reliability**: Dedicated Python package vs complex YAML logic
- **Maintainability**: All environment logic centralized in showtime CLI
- **Efficiency**: Conditional builds and smart resource management
- **Testability**: Manual workflow_dispatch for development and debugging

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-25 13:23:22 -07:00
4 changed files with 252 additions and 5 deletions

View File

@@ -1,4 +1,10 @@
name: Cleanup ephemeral envs (PR close)
name: Cleanup ephemeral envs (PR close) [DEPRECATED]
# ⚠️ DEPRECATION NOTICE ⚠️
# This workflow is deprecated and will be removed in a future version.
# The new Superset Showtime workflow handles cleanup automatically.
# See .github/workflows/showtime.yml and showtime-cleanup.yml for replacements.
# Migration guide: https://github.com/mistercrunch/superset-showtime
on:
pull_request_target:
@@ -71,5 +77,5 @@ jobs:
issue_number: ${{ github.event.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: 'Ephemeral environment shutdown and build artifacts deleted.'
body: '⚠️ **DEPRECATED WORKFLOW** - Ephemeral environment shutdown and build artifacts deleted. Please migrate to the new Superset Showtime system for future PRs.'
})

View File

@@ -1,4 +1,12 @@
name: Ephemeral env workflow
name: Ephemeral env workflow [DEPRECATED]
# ⚠️ DEPRECATION NOTICE ⚠️
# This workflow is deprecated and will be removed in a future version.
# Please use the new Superset Showtime workflow instead:
# - Use label "🎪 trigger-start" instead of "testenv-up"
# - Showtime provides better reliability and easier management
# - See .github/workflows/showtime.yml for the replacement
# - Migration guide: https://github.com/mistercrunch/superset-showtime
# Example manual trigger:
# gh workflow run ephemeral-env.yml --ref fix_ephemerals --field label_name="testenv-up" --field issue_number=666
@@ -126,8 +134,11 @@ jobs:
throw new Error("Issue number is not available.");
}
const body = `@${user} Processing your ephemeral environment request [here](${workflowUrl}).` +
` Action: **${action}**.` +
const body = `⚠️ **DEPRECATED WORKFLOW** ⚠️\n\n@${user} This workflow is deprecated! Please use the new **Superset Showtime** system instead:\n\n` +
`- Replace "testenv-up" label with "🎪 trigger-start"\n` +
`- Better reliability and easier management\n` +
`- See https://github.com/mistercrunch/superset-showtime for details\n\n` +
`Processing your ephemeral environment request [here](${workflowUrl}). Action: **${action}**.` +
` More information on [how to use or configure ephemeral environments]` +
`(https://superset.apache.org/docs/contributing/howtos/#github-ephemeral-environments)`;

50
.github/workflows/showtime-cleanup.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: 🎪 Showtime Cleanup
# Scheduled cleanup of expired environments
on:
schedule:
- cron: '0 */6 * * *' # Every 6 hours
# Manual trigger for testing
workflow_dispatch:
inputs:
max_age_hours:
description: 'Maximum age in hours before cleanup'
required: false
default: '48'
type: string
# Common environment variables
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ vars.AWS_REGION || 'us-west-2' }}
GITHUB_ORG: ${{ github.repository_owner }}
GITHUB_REPO: ${{ github.event.repository.name }}
jobs:
cleanup-expired:
name: Clean up expired showtime environments
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- name: Install Superset Showtime
run: pip install superset-showtime
- name: Cleanup expired environments
run: |
MAX_AGE="${{ github.event.inputs.max_age_hours || '48' }}"
# Validate max_age is numeric
if [[ ! "$MAX_AGE" =~ ^[0-9]+$ ]]; then
echo "❌ Invalid max_age_hours format: $MAX_AGE (must be numeric)"
exit 1
fi
echo "Cleaning up environments older than ${MAX_AGE}h"
python -m showtime cleanup --older-than "${MAX_AGE}h"

180
.github/workflows/showtime-trigger.yml vendored Normal file
View File

@@ -0,0 +1,180 @@
name: 🎪 Superset Showtime
# Ultra-simple: just sync on any PR state change
on:
pull_request_target:
types: [labeled, unlabeled, synchronize, closed]
# Manual testing
workflow_dispatch:
inputs:
pr_number:
description: 'PR number to sync'
required: true
type: number
sha:
description: 'Specific SHA to deploy (optional, defaults to latest)'
required: false
type: string
# Common environment variables for all jobs (non-sensitive only)
env:
AWS_REGION: us-west-2
GITHUB_ORG: ${{ github.repository_owner }}
GITHUB_REPO: ${{ github.event.repository.name }}
GITHUB_ACTOR: ${{ github.actor }}
jobs:
sync:
name: 🎪 Sync PR to desired state
runs-on: ubuntu-latest
timeout-minutes: 90
permissions:
contents: read
pull-requests: write
steps:
- name: Security Check - Authorize Maintainers Only
id: auth
uses: actions/github-script@v7
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
script: |
const actor = context.actor;
console.log(`🔍 Checking authorization for ${actor}`);
// Early exit for workflow_dispatch - assume authorized since it's manually triggered
if (context.eventName === 'workflow_dispatch') {
console.log(`✅ Workflow dispatch event - assuming authorized for ${actor}`);
core.setOutput('authorized', 'true');
return;
}
const { data: permission } = await github.rest.repos.getCollaboratorPermissionLevel({
owner: context.repo.owner,
repo: context.repo.repo,
username: actor
});
console.log(`📊 Permission level for ${actor}: ${permission.permission}`);
const authorized = ['write', 'admin'].includes(permission.permission);
if (!authorized) {
console.log(`🚨 Unauthorized user ${actor} - checking if we need to block operations`);
// If this is a synchronize event with existing showtime environments, block it
if (context.eventName === 'pull_request_target' && context.payload.action === 'synchronize') {
console.log(`🔒 Unauthorized synchronize event detected - checking if Showtime is active`);
// Check if PR has any circus tent labels (Showtime is in use)
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number
});
const hasCircusLabels = issue.labels.some(label => label.name.startsWith('🎪 '));
if (hasCircusLabels) {
console.log(`🎪 Circus labels found - setting blocked label to prevent unauthorized auto-deployment`);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
labels: ['🎪 🔒 showtime-blocked']
});
console.log(`✅ Blocked label set - unauthorized user cannot auto-deploy changes`);
} else {
console.log(` No circus labels found - Showtime not in use, skipping block`);
}
}
core.setOutput('authorized', 'false');
return;
}
console.log(`✅ Authorized maintainer: ${actor} - allowing all operations including synchronize`);
core.setOutput('authorized', 'true');
- name: Install Superset Showtime
if: steps.auth.outputs.authorized == 'true'
run: |
echo "::notice::Maintainer ${{ github.actor }} triggered deploy for PR ${{ github.event.pull_request.number || github.event.inputs.pr_number }}"
pip install --upgrade superset-showtime
showtime version
- name: Check what actions are needed
if: steps.auth.outputs.authorized == 'true'
id: check
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Bulletproof PR number extraction
if [[ -n "${{ github.event.pull_request.number }}" ]]; then
PR_NUM="${{ github.event.pull_request.number }}"
elif [[ -n "${{ github.event.inputs.pr_number }}" ]]; then
PR_NUM="${{ github.event.inputs.pr_number }}"
else
echo "❌ No PR number found in event or inputs"
exit 1
fi
echo "Using PR number: $PR_NUM"
# Run sync check-only with optional SHA override
if [[ -n "${{ github.event.inputs.sha }}" ]]; then
OUTPUT=$(python -m showtime sync $PR_NUM --check-only --sha "${{ github.event.inputs.sha }}")
else
OUTPUT=$(python -m showtime sync $PR_NUM --check-only)
fi
echo "$OUTPUT"
# Extract the outputs we need for conditional steps
BUILD=$(echo "$OUTPUT" | grep "build_needed=" | cut -d'=' -f2)
SYNC=$(echo "$OUTPUT" | grep "sync_needed=" | cut -d'=' -f2)
PR_NUM_OUT=$(echo "$OUTPUT" | grep "pr_number=" | cut -d'=' -f2)
TARGET_SHA=$(echo "$OUTPUT" | grep "target_sha=" | cut -d'=' -f2)
echo "build_needed=$BUILD" >> $GITHUB_OUTPUT
echo "sync_needed=$SYNC" >> $GITHUB_OUTPUT
echo "pr_number=$PR_NUM_OUT" >> $GITHUB_OUTPUT
echo "target_sha=$TARGET_SHA" >> $GITHUB_OUTPUT
- name: Checkout PR code (only if build needed)
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
uses: actions/checkout@v4
with:
ref: ${{ steps.check.outputs.target_sha }}
persist-credentials: false
- name: Setup Docker Environment (only if build needed)
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.build_needed == 'true'
uses: ./.github/actions/setup-docker
with:
dockerhub-user: ${{ secrets.DOCKERHUB_USER }}
dockerhub-token: ${{ secrets.DOCKERHUB_TOKEN }}
build: "true"
install-docker-compose: "false"
- name: Execute sync (handles everything)
if: steps.auth.outputs.authorized == 'true' && steps.check.outputs.sync_needed == 'true'
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: |
PR_NUM="${{ steps.check.outputs.pr_number }}"
TARGET_SHA="${{ steps.check.outputs.target_sha }}"
if [[ -n "$TARGET_SHA" ]]; then
python -m showtime sync $PR_NUM --sha "$TARGET_SHA"
else
python -m showtime sync $PR_NUM
fi