| 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@v8 |
| 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 this is a synchronize event from unauthorized user, check if Showtime is active and set blocked label |
| if (!authorized && context.eventName === 'pull_request_target' && context.payload.action === 'synchronize') { |
| console.log(`đ 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 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 - Showtime will detect and skip operations`); |
| } else { |
| console.log(`âšī¸ No circus labels found - Showtime not in use, skipping block`); |
| } |
| } |
| |
| if (!authorized) { |
| console.log(`đ¨ Unauthorized user ${actor} - skipping all operations`); |
| core.setOutput('authorized', 'false'); |
| return; |
| } |
| |
| console.log(`â
Authorized maintainer: ${actor}`); |
| 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@v5 |
| 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 |