| # Python unit tests |
| name: Python-Unit |
| |
| # Least-privilege default for GITHUB_TOKEN. Jobs that need more (e.g. OIDC for |
| # codecov uploads) opt in via their own job-level `permissions:` block. |
| permissions: |
| contents: read |
| |
| on: |
| push: |
| branches: |
| - "master" |
| - "[0-9].[0-9]*" |
| pull_request: |
| types: [synchronize, opened, reopened, ready_for_review] |
| |
| # cancel previous workflow jobs for PRs |
| concurrency: |
| group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.run_id }} |
| cancel-in-progress: true |
| |
| jobs: |
| changes: |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 10 |
| permissions: |
| contents: read |
| pull-requests: read |
| outputs: |
| python: ${{ steps.check.outputs.python }} |
| steps: |
| - name: Checkout |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| with: |
| persist-credentials: false |
| - name: Check for file changes |
| id: check |
| uses: ./.github/actions/change-detector/ |
| with: |
| token: ${{ secrets.GITHUB_TOKEN }} |
| |
| unit-tests: |
| needs: changes |
| if: needs.changes.outputs.python == 'true' |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 30 |
| permissions: |
| id-token: write |
| strategy: |
| matrix: |
| # Full version spread on push (master/release) + nightly; current only |
| # on PRs to cut runner cost (cross-version breaks are caught at merge). |
| python-version: ${{ github.event_name == 'pull_request' && fromJSON('["current"]') || fromJSON('["previous", "current", "next"]') }} |
| env: |
| PYTHONPATH: ${{ github.workspace }} |
| steps: |
| - name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )" |
| uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 |
| with: |
| persist-credentials: false |
| submodules: recursive |
| - name: Setup Python |
| uses: ./.github/actions/setup-backend/ |
| with: |
| python-version: ${{ matrix.python-version }} |
| - name: Python unit tests |
| env: |
| SUPERSET_TESTENV: true |
| SUPERSET_SECRET_KEY: not-a-secret |
| run: | |
| pytest --durations-min=0.5 --cov-report= --cov=superset ./tests/common ./tests/unit_tests --cache-clear --maxfail=50 |
| - name: Python 100% coverage unit tests |
| env: |
| SUPERSET_TESTENV: true |
| SUPERSET_SECRET_KEY: not-a-secret |
| run: | |
| pytest --durations-min=0.5 --cov=superset/sql/ ./tests/unit_tests/sql/ --cache-clear --cov-fail-under=100 |
| pytest --durations-min=0.5 --cov=superset/semantic_layers/ ./tests/unit_tests/semantic_layers/ --cache-clear --cov-fail-under=100 |
| - name: Upload code coverage |
| uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 |
| with: |
| flags: python,unit |
| verbose: true |
| use_oidc: true |
| slug: apache/superset |
| |
| # Stable required-status-check anchor. `unit-tests` is a matrix job gated on |
| # change detection, so on non-Python PRs it is skipped and never produces its |
| # `unit-tests (current)` context (a job-level skip happens before matrix |
| # expansion). This always-running job reports a single context that branch |
| # protection can require: it passes when unit-tests succeeded or was skipped, |
| # and fails only on a real failure. |
| unit-tests-required: |
| needs: [changes, unit-tests] |
| if: always() |
| runs-on: ubuntu-24.04 |
| timeout-minutes: 5 |
| steps: |
| - name: Check unit-tests result |
| env: |
| RESULT: ${{ needs.unit-tests.result }} |
| run: | |
| if [ "$RESULT" != "success" ] && [ "$RESULT" != "skipped" ]; then |
| echo "unit-tests did not pass (result: $RESULT)" |
| exit 1 |
| fi |
| echo "unit-tests result: $RESULT" |