diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 04c17f536..fddad0374 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,16 +8,18 @@ on: - 'tests/**' - 'pyproject.toml' - '.github/workflows/**' + # No paths filter here: lint and test are required checks, and a workflow that + # never triggers never reports, leaving such PRs permanently blocked. The filter + # lives in the `changes` job below, which skips the expensive steps instead. pull_request: types: [opened, synchronize, reopened, ready_for_review] branches: ["**"] - paths: - - 'flixopt/**' - - 'tests/**' - - 'pyproject.toml' - - '.github/workflows/**' workflow_dispatch: +permissions: + contents: read + pull-requests: read # `changes` reads the PR's file list via gh + concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true @@ -30,6 +32,37 @@ env: FLIXOPT_CI: false jobs: + changes: + name: Detect source changes + runs-on: ubuntu-24.04 + outputs: + source: ${{ steps.detect.outputs.source }} + steps: + - name: Detect source changes + id: detect + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + if [[ "${{ github.event_name }}" != 'pull_request' ]]; then + echo 'source=true' >> "$GITHUB_OUTPUT" + exit 0 + fi + # Treat a failed, empty or truncated listing as a source change, so a + # detection failure costs a test run rather than skipping one. The guard + # is required: steps run under `bash -e`, where a failing command + # substitution aborts the step instead of falling through. + if ! paths=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json files --jq '.files[].path'); then + echo 'source=true' >> "$GITHUB_OUTPUT" + exit 0 + fi + if [[ -z "$paths" ]] || grep -qE '^(flixopt/|tests/|pyproject\.toml$|\.github/workflows/)' <<< "$paths"; then + echo 'source=true' >> "$GITHUB_OUTPUT" + else + echo 'source=false' >> "$GITHUB_OUTPUT" + fi + lint: runs-on: ubuntu-24.04 steps: @@ -52,7 +85,7 @@ jobs: test: runs-on: ubuntu-24.04 timeout-minutes: 30 - needs: lint + needs: [lint, changes] strategy: fail-fast: false matrix: @@ -70,9 +103,11 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install dependencies + if: needs.changes.outputs.source == 'true' run: uv pip install --system .[dev] - name: Run tests + if: needs.changes.outputs.source == 'true' run: | if [[ "${{ github.event.pull_request.draft }}" == "true" ]]; then # Draft PR: skip examples, slow, and deprecated_api @@ -82,6 +117,10 @@ jobs: pytest -v --numprocesses=auto fi + - name: Report skipped tests + if: needs.changes.outputs.source != 'true' + run: echo 'No changes under flixopt/, tests/, pyproject.toml or .github/workflows/ - tests skipped.' + test-examples: runs-on: ubuntu-24.04 timeout-minutes: 45