Skip to content

feat: skip expensive build steps when source files unchanged (dorny/paths-filter)#14713

Draft
Copilot wants to merge 2 commits intomasterfrom
copilot/add-paths-filter-to-matrix-jobs
Draft

feat: skip expensive build steps when source files unchanged (dorny/paths-filter)#14713
Copilot wants to merge 2 commits intomasterfrom
copilot/add-paths-filter-to-matrix-jobs

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 1, 2026

Each matrix job in sphinxbuild.yml rebuilds unconditionally on every PR/push. This adds per-manual change detection so Python setup, pip install, build, and artifact upload are skipped entirely when nothing relevant changed.

Changes

  • build-html + build-pdf: Add dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 (v3.0.2) as step 2 in each matrix job; all expensive steps gated on steps.filter.outputs.changed == 'true'

    • HTML filter: ${{ matrix.manual.directory }}/**, requirements.txt, .github/workflows/sphinxbuild.yml
    • PDF filter: same + .devcontainer/Dockerfile
    • Upload static documentation condition: ${{ matrix.manual.publish && steps.filter.outputs.changed == 'true' }}
  • link-check: Replace hardcoded lychee glob args with a find-based path list written to /tmp/lychee-paths.txt, passed via @/tmp/lychee-paths.txt. Avoids lychee errors when a manual directory was never staged because its build was skipped.

- name: Build lychee path list
  run: |
    branch="${{ needs.stage-and-check.outputs.branch_name }}"
    : > /tmp/lychee-paths.txt
    [ -d "stage/${branch}/user_manual" ]     && find "stage/${branch}/user_manual/en"     -name '*.html' >> /tmp/lychee-paths.txt || true
    [ -d "stage/${branch}/admin_manual" ]    && find "stage/${branch}/admin_manual"        -name '*.html' >> /tmp/lychee-paths.txt || true
    [ -d "stage/${branch}/developer_manual" ] && find "stage/${branch}/developer_manual"  -name '*.html' >> /tmp/lychee-paths.txt || true

The on: trigger, prepare-pdf-image, stage-and-check, deploy, and summary jobs are untouched.

☑️ Resolves

🖼️ Screenshots

No visual output changes.

✅ Checklist

  • I have built the documentation locally and reviewed the output
  • Screenshots are included for visual changes
  • I have not moved or renamed pages (or added a redirect if I did)
  • I have run codespell or similar and addressed any spelling issues
Original prompt

Goal

In .github/workflows/sphinxbuild.yml, use dorny/paths-filter inside each matrix job to skip all expensive steps (Python setup, pip install, build, upload) when the relevant source files haven't changed. The workflow trigger should remain untouched (still runs on all PRs and pushes to master/stable*).

Changes needed

1. build-html job

Add dorny/paths-filter as the second step (after checkout), filtering on:

  • ${{ matrix.manual.directory }}/**
  • requirements.txt
  • .github/workflows/sphinxbuild.yml

Gate all subsequent steps (Set up Python, Install pip dependencies, Build html documentation, Upload static documentation) on steps.filter.outputs.changed == 'true'.

The Upload static documentation step currently has if: ${{ matrix.manual.publish }} — this should become if: matrix.manual.publish && steps.filter.outputs.changed == 'true'.

2. build-pdf job

Same pattern. Add dorny/paths-filter as the second step, filtering on:

  • ${{ matrix.manual.directory }}/**
  • requirements.txt
  • .devcontainer/Dockerfile
  • .github/workflows/sphinxbuild.yml

Gate all subsequent steps on steps.filter.outputs.changed == 'true'.

3. link-check job

The lychee args currently hardcode globs for all three manuals. If a manual was skipped (no artifact), its folder won't exist in stage/ and lychee may fail. Make each glob conditional on whether the directory actually exists, for example using a shell find or [ -d ... ] check before the lychee step, and only pass globs for directories that exist.

Specifically, add a step before lychee that builds the list of paths to check into an env var or file, then pass that to the lychee args. Or use a simpler approach: use find to discover all relevant *.html files and pass the list, skipping missing directories.

4. Pin dorny/paths-filter

Use a pinned commit SHA for dorny/paths-filter, similar to how all other actions in this file are pinned. Use v3 — the SHA for dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 is correct for v3.0.2.

Constraints

  • Do NOT change the on: trigger block.
  • Do NOT change the prepare-pdf-image job — it already has its own Dockerfile change detection.
  • Do NOT change stage-and-check, deploy, or summary jobs.
  • Keep all existing pinned SHAs and comments intact.
  • Preserve all existing inline comments and section banners.

The following is the prior conversation context from the user's chat exploration (may be truncated):

User: So, we build the documentation every time on PR.
For faster builds, is it possible to do some kind of hash or something and only compile if the content of the manual change ?

Assistant: Yes! GitHub Actions has a built-in, idiomatic solution for this: paths / paths-ignore filters on the workflow trigger. This is simpler and more reliable than computing hashes yourself.

The approach: paths filter on the trigger

You add a paths filter to the on: block. GitHub will then only trigger the workflow when at least one of those matching files changed in the PR/push.

on:
  pull_request:
    paths:
      - 'user_manual/**'
      - 'admin_manual/**'
      - 'developer_manual/**'
      - 'requirements.txt'
      - '.devcontainer/Dockerfile'
      - '.github/workflows/sphinxbuild.yml'  # re-run if the workflow itself changes
  push:
    branches:
      - master
      - stable*
    paths:
      - 'user_manual/**'
      - 'admin_manual/**'
      - 'developer_manual/**'
      - 'requirements.txt'
      - '.devcontainer/Dockerfile'
      - '.github/workflows/sphinxbuild.yml'

What this gives you

Scenario Behavior
PR only touches user_manual/** Workflow runs ✅
PR only touches .github/workflows/other.yml Workflow skipped ⏭️
PR touches requirements.txt Workflow runs ✅ (deps changed)
PR touches .devcontainer/Dockerfile Workflow runs ✅ (PDF image may need rebuild)

⚠️ One caveat: required status checks

If sphinxbuild.yml jobs are set as required checks on branch protection, a skipped run won't satisfy the requirement by default. You have two options:

  1. Use the existing summary job pattern you already have — make the summary job the required check. When the workflow is skipped entirely, you can add a separate small "skip" workflow that uses paths-ignore inversely and just posts a passing status. This is the ["always-passing required check" pattern](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/troubleshooting-required-status-checks#handling-skipped-...

This pull request was created from Copilot chat.

Copilot AI changed the title [WIP] Add dorny/paths-filter to skip expensive steps in Sphinx build feat: skip expensive build steps when source files unchanged (dorny/paths-filter) May 1, 2026
Copilot AI requested a review from skjnldsv May 1, 2026 17:20
@github-actions github-actions Bot added the github_actions Pull requests that update GitHub Actions code label May 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

github_actions Pull requests that update GitHub Actions code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants