From ef1978f2d29631310609f3de5c0b8f078d3743ef Mon Sep 17 00:00:00 2001 From: Tania Allard Date: Fri, 24 May 2024 16:41:42 +0100 Subject: [PATCH] DEV - Refactor CI and environment setup (#1759) This PR addresses some of the concerns/issues raised in #1292 --- .devcontainer/devcontainer.json | 1 + .github/actions/set-dev-env/action.yml | 32 +++ .github/prerelease-template.md | 3 +- .github/workflows/CI.yml | 248 +++++++++++++++++++++++ .github/workflows/prerelease.yml | 39 ++-- .github/workflows/publish.yml | 7 +- .github/workflows/tests.yml | 268 ------------------------- docs/community/setup.md | 99 +++++---- docs/community/topics/accessibility.md | 4 +- docs/community/topics/assets.md | 2 +- docs/community/topics/manual-dev.md | 17 +- pyproject.toml | 12 +- tests/README.md | 14 +- tools/github_actions_install.sh | 18 -- tools/profile.py | 99 +++++++++ tox.ini | 127 ++++++++++++ 16 files changed, 619 insertions(+), 371 deletions(-) create mode 100644 .github/actions/set-dev-env/action.yml create mode 100644 .github/workflows/CI.yml delete mode 100644 .github/workflows/tests.yml delete mode 100755 tools/github_actions_install.sh create mode 100644 tools/profile.py create mode 100644 tox.ini diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 5f99475b8..0a7d0b0ba 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -3,6 +3,7 @@ "name": "Python 3", "image": "mcr.microsoft.com/devcontainers/python:1-3.10-bullseye", "features": { + "ghcr.io/devcontainers-contrib/features/tox:2": {}, "ghcr.io/devcontainers-contrib/features/nox:2": {}, "ghcr.io/devcontainers-contrib/features/pre-commit:2": {}, "ghcr.io/rocker-org/devcontainer-features/pandoc:1": {}, diff --git a/.github/actions/set-dev-env/action.yml b/.github/actions/set-dev-env/action.yml new file mode 100644 index 000000000..b5398c8eb --- /dev/null +++ b/.github/actions/set-dev-env/action.yml @@ -0,0 +1,32 @@ +name: Setup PST CI environment +description: Create a PST dev environment + +inputs: + python-version: + description: Default Python version to use if none is specified + required: false + default: "3.12" + pandoc: + description: Whether this should install pandoc or not + required: false + default: "False" + +runs: + using: composite + steps: + - name: "Setup Python ๐Ÿ" + uses: actions/setup-python@v5 + with: + python-version: ${{ inputs.python-version }} + cache: "pip" + cache-dependency-path: "pyproject.toml" + allow-prereleases: true + + - run: python -Im pip install tox-uv + shell: bash + + # waiting for https://github.com/nikeee/setup-pandoc/pull/8 + # using 12rambau fork until then + - name: "Install pandoc ๐Ÿ“" + uses: 12rambau/setup-pandoc@test + if: ${{ inputs.pandoc }} == true diff --git a/.github/prerelease-template.md b/.github/prerelease-template.md index ff8e27c10..a127a1e6e 100644 --- a/.github/prerelease-template.md +++ b/.github/prerelease-template.md @@ -4,6 +4,7 @@ assignees: choldgraf labels: bug, enhancement --- -A prerelease of one of our dependencies failed. See the +A prerelease of one of our dependencies failed. +See the [action log](https://github.com/{{ env.GITHUB_ACTION_REPOSITORY }}/actions/runs/{{ env.GITHUB_RUN_ID }}) for more details. diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 000000000..6cfebab9e --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,248 @@ +name: continuous-integration-tox + +# Concurrency group that uses the workflow name and PR number if available +# or commit SHA as a fallback. If a new build is triggered under that +# concurrency group while a previous build is running it will be canceled. +# Repeated pushes to a PR will cancel all previous builds, while multiple +# merges to main will not cancel. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true + +env: + FORCE_COLOR: "1" # Make tools pretty + DEFAULT_PYTHON_VERSION: "3.12" # keep in sync with tox.ini + PIP_DISABLE_PIP_VERSION_CHECK: "1" # Don't check for pip updates + +permissions: {} + +on: + push: + branches: + - main + pull_request: + workflow_call: + # allow manual triggering of the workflow, while debugging + workflow_dispatch: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: "Run lint checks ๐Ÿงน" + run: python -Im tox run -e lint + + # Run our test suite on various combinations of OS & Python versions + run-pytest: + needs: lint + strategy: + fail-fast: true + matrix: + # macos-14==latest + # ubuntu-20.04==latest + os: + [ + "ubuntu-latest", + "ubuntu-24.04", + "macos-14", + "macos-13", + "windows-latest", + ] + python-version: ["3.9", "3.10", "3.11", "3.12"] + sphinx-version: [""] + include: + # oldest Python version with the oldest Sphinx version + - os: ubuntu-latest + python-version: "3.9" + sphinx-version: "6.1" + # newest Python version with the newest Sphinx version + - os: ubuntu-latest + python-version: "3.12" + # Sphinx HEAD + sphinx-version: "dev" + exclude: + # Python 3.9 is not supported on macOS 14 - https://raw.githubusercontent.com/actions/python-versions/main/versions-manifest.json + - os: macos-14 + python-version: "3.9" + # do not need all the tests so will limit to the latest versions of Python + - os: ubuntu-24.04 + python-version: "3.9" + - os: ubuntu-24.04 + python-version: "3.10" + runs-on: ${{ matrix.os }} + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ matrix.python-version }} + pandoc: true + - name: "Run tests โœ…" + shell: bash + run: | + # this will compile the assets then run the tests + # check if there is a specific Sphinx version to test with + # example substitution: tox run -e compile,py39-sphinx61-tests + if [ -n "${{matrix.sphinx-version}}" ]; then + python -Im tox run -e compile,py$(echo ${{ matrix.python-version }} | tr -d .)-sphinx$(echo ${{ matrix.sphinx-version }} | tr -d .)-tests + # if not we use the default version + # example substitution: tox run -e compile,py39-tests + else + python -Im tox run -e compile,py$(echo ${{ matrix.python-version }} | tr -d .)-tests + fi + - name: "Upload coverage data to GH artifacts ๐Ÿ“ค" + if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest' && matrix.sphinx-version == 'dev' + uses: actions/upload-artifact@v4 + with: + name: coverage-data-${{ matrix.python-version }} + path: .coverage + if-no-files-found: ignore + + # Only run accessibility tests on the latest Python version (3.12) and Ubuntu + a11y-tests: + name: "a11y-tests (ubuntu-latest, 3.12)" + needs: lint + runs-on: ubuntu-latest + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: "Run accessibility tests with playwright ๐ŸŽญ" + # build PST, build docs, then run a11y-tests + run: python -Im tox run -e py312-docs,a11y-tests + continue-on-error: true + + # Build our docs (PST) on major OSes and check for warnings + build-site: + name: "build PST docs" + needs: lint + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + python-version: ["3.12"] + include: + # oldest Python version with the oldest Sphinx version + - os: ubuntu-latest + python-version: "3.9" + sphinx-version: "6.1" + runs-on: ${{ matrix.os }} + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ matrix.python-version }} + pandoc: true + - name: "Build docs and check for warnings ๐Ÿ“–" + shell: bash + run: | + # check if there is a specific Sphinx version to build with + # example substitution: tox run -e docs-py39-sphinx61-docs + if [ -n "${{matrix.sphinx-version}}" ]; then + python -Im tox run -e py$(echo ${{ matrix.python-version }} | tr -d .)-sphinx$(echo ${{ matrix.sphinx-version }} | tr -d .)-docs + # build with the default Sphinx version + # example substitution: tox run -e docs-py312-docs + else + python -Im tox run -e py$(echo ${{ matrix.python-version }} | tr -d .)-docs + fi + + # Run Lighthouse audits on the built site (kitchen-sink only) + lighthouse-audit: + needs: build-site + runs-on: ubuntu-latest + env: + DOCS_DIR: "audit" + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - name: "Copy kitchen sink to a tiny site" + run: | + mkdir -p ${{ env.DOCS_DIR }}/site + cp -r docs/examples/kitchen-sink ${{ env.DOCS_DIR }}/site/kitchen-sink + printf "Test\n====\n\n.. toctree::\n\n kitchen-sink/index\n" > ${{ env.DOCS_DIR }}/site/index.rst + echo 'html_theme = "pydata_sphinx_theme"' > ${{ env.DOCS_DIR }}/site/conf.py + echo '.. toctree::\n :glob:\n\n *' >> ${{ env.DOCS_DIR }}/site/index.rst + + # build docs without checking for warnings + python -Im tox run -e docs-no-checks + + - name: "Audit with Lighthouse ๐Ÿ”ฆ" + uses: treosh/lighthouse-ci-action@v11 + with: + configPath: ".github/workflows/lighthouserc.json" + temporaryPublicStorage: true + uploadArtifacts: true + runs: 3 # Multiple runs to reduce variance + + coverage: + name: "check coverage" + needs: run-pytest + runs-on: ubuntu-latest + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + python-version: ${{ env.DEFAULT_PYTHON_VERSION }} + - run: python -Im pip install --upgrade coverage[toml] + - name: "Download coverage data ๐Ÿ“ฅ" + uses: actions/download-artifact@v4 + with: + pattern: coverage-data-* + merge-multiple: true + - name: "Get coverage data & fail if it's <80%" + run: | + # if we decide to check cov across versions and combine + # python -Im coverage combine + python -Im coverage html --skip-covered --skip-empty + + # report and write to summary. + python -Im coverage report --format=markdown >> $GITHUB_STEP_SUMMARY + + # report again and fail if under 80%. + python -Im coverage report --fail-under=80 + - name: "Upload HTML report if check failed ๐Ÿ“ค" + uses: actions/upload-artifact@v4 + with: + name: html-report + path: htmlcov + if: ${{ failure() }} + + profiling: + needs: [build-site, run-pytest] + runs-on: ubuntu-latest + steps: + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env + with: + # 3.12 is not supported by py-spy yet + python-version: "3.11" + - name: "Run profiling with py-spy ๐Ÿ•ต๏ธโ€โ™‚๏ธ" + # profiling needs to be run as sudo + run: python -m tox run -e py311-profile-docs -- -o docbuild_profile.svg + continue-on-error: true + - name: "Upload profiling data to GH artifacts ๐Ÿ“ค" + uses: actions/upload-artifact@v4 + with: + name: profile-results + path: docbuild_profile.svg + if-no-files-found: ignore diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 967322248..eee2fa340 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -7,6 +7,10 @@ on: - cron: "0 5 * * 1,4" workflow_dispatch: +env: + FORCE_COLOR: "1" # Make tools pretty + PIP_DISABLE_PIP_VERSION_CHECK: "1" # Don't check for pip updates + jobs: prerelease: runs-on: ubuntu-latest @@ -16,35 +20,28 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v4 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 + - name: "Checkout repository ๐Ÿ›Ž" + uses: actions/checkout@v4 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env with: python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - - - name: Install (prerelease) dependencies + pandoc: "True" + - name: "Install (prerelease) dependencies ๐Ÿ“ฆ" run: | - python -m pip install --upgrade pip wheel setuptools - python -m pip install --upgrade --pre -e .[doc,test] - - - name: Build docs to store + python -Im pip install --upgrade pip wheel setuptools + - name: "Build PST docs and check for warnings ๐Ÿ“–" run: | - sphinx-build -b html docs/ docs/_build/html --keep-going -w warnings.txt - - - name: Check that there are no unexpected Sphinx warnings - if: matrix.python-version == '3.10' - run: python tests/utils/check_warnings.py - - - name: Run the tests + # example substitution: tox run -e docs-py312-docs + python -Im tox run -e docs-py$(echo ${{ matrix.python-version }} | tr -d .)-docs -- --keep-going + - name: "Run tests โœ… (no coverage)" run: | - pytest --color=yes + # this will compile the assets then run the tests + python -Im tox run -e compile,py$(echo ${{ matrix.python-version }} | tr -d .)-tests-no-cov echo "PYTEST_ERRORS=$?" >> $GITHUB_ENV # If either the docs build or the tests resulted in an error, create an issue to note it - - name: Create an issue if failure + - name: "Create an issue if failure" uses: JasonEtco/create-an-issue@v2 if: ${{ env.SPHINX_BUILD_UNEXPECTED_WARNINGS || !env.PYTEST_ERRORS }} env: diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index ff973840e..3678ae616 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -17,7 +17,7 @@ permissions: jobs: # calls our tests workflow tests: - uses: ./.github/workflows/tests.yml + uses: ./.github/workflows/CI.yml build-package: name: "Build & verify PST package" @@ -27,10 +27,11 @@ jobs: - name: "Checkout repository ๐Ÿ›Ž" uses: actions/checkout@v4 - - name: "Set up Python 3.9" - uses: actions/setup-python@v5 + - name: "Setup CI environment ๐Ÿ› " + uses: ./.github/actions/set-dev-env with: python-version: "3.9" + pandoc: "False" - name: "Install gettext for translations ๐ŸŒ" run: | diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml deleted file mode 100644 index cd5f7ceaa..000000000 --- a/.github/workflows/tests.yml +++ /dev/null @@ -1,268 +0,0 @@ -name: continuous-integration -concurrency: - group: ${{ github.workflow }}-${{ github.event.number }}-${{ github.event.ref }} - cancel-in-progress: true - -# README -# ====== -# -# All the jobs are defined with `matrix` for OS and Python version, even if we -# only run them on one combination of OS/Python. The reason for this is you get -# a nice side-effect that the OS and Python version of the job are listed in -# parentheses next to the job name in the Actions UI. - -# This prevents workflows from being run twice on PRs -# ref: https://github.community/t/how-to-trigger-an-action-on-push-or-pull-request-but-not-both/16662 -on: - push: - branches: - - main - pull_request: - workflow_call: - -jobs: - lint: - timeout-minutes: 3 - strategy: - matrix: - os: [ubuntu-latest] - python-version: ["3.12"] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - - uses: pre-commit/action@v3.0.1 - - # run our test suite on various combinations of OS / Python / Sphinx version - run-pytest: - timeout-minutes: 10 - needs: [lint] - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest] - python-version: ["3.9", "3.10", "3.11", "3.12"] - sphinx-version: ["rls"] - include: - # macos test - - os: macos-latest - python-version: "3.12" - sphinx-version: "rls" - # windows test - - os: windows-latest - python-version: "3.12" - sphinx-version: "rls" - # old Sphinx test - - os: ubuntu-latest - python-version: "3.9" - sphinx-version: "old" - # dev Sphinx test - - os: ubuntu-latest - python-version: "3.12" - sphinx-version: "dev" - # needed to cache the browsers for the accessibility tests - env: - PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/pw-browsers - SPHINX_VERSION: ${{ matrix.sphinx-version }} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - # waiting for https://github.com/nikeee/setup-pandoc/pull/8 - # using 12rambau fork until then - - uses: 12rambau/setup-pandoc@test - - name: Install dependencies - # if Sphinx version not specified in matrix, the constraints in the - # pyproject.toml file determine Sphinx version - shell: bash - # setting shell to BASH and using PYTHONUTF8 env var makes the editable - # install work on Windows even though there are emoji in our README - run: tools/github_actions_install.sh test - - name: Show installed versions - run: python -m pip list - - name: Compile MO files - run: | - pip install nox - nox -s compile - - name: Run tests - run: pytest -m "not a11y" --color=yes --cov --cov-report=xml - env: - COVERAGE_FILE: ".coverage.${{ matrix.python-version }}.${{ matrix.os }}.${{ matrix.sphinx-version }}" - - name: Store coverage file - uses: actions/upload-artifact@v4 - with: - name: coverage-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.sphinx-version }} - path: .coverage.${{ matrix.python-version }}.${{ matrix.os }}.${{ matrix.sphinx-version }} - # note I am setting this on top of the Python cache as I could not find - # how to set the hash key on the python one - - name: Set up browser cache (for accessibility tests) - uses: actions/cache@v4 - with: - path: | - ${{ github.workspace }}/pw-browsers - key: ${{ runner.os }}-pw-${{ hashFiles('pyproject.toml') }} - restore-keys: | - ${{ runner.os }}-pw- - - - name: Run accessibility tests with playwright - if: matrix.python-version == '3.12' && matrix.os == 'ubuntu-latest' - run: | - nox -s a11y - continue-on-error: true - - coverage: - name: Collect Coverage - runs-on: ubuntu-latest - needs: run-pytest - # run both on previous step success and failure - if: ${{ !cancelled() }} - permissions: - pull-requests: write - contents: write - steps: - - uses: actions/checkout@v4 - - - uses: actions/download-artifact@v4 - id: download - with: - pattern: coverage-* - merge-multiple: true - - - name: Coverage comment - id: coverage_comment - uses: py-cov-action/python-coverage-comment-action@v3 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - MERGE_COVERAGE_FILES: true - - - name: Store Pull Request comment to be posted - uses: actions/upload-artifact@v4 - if: steps.coverage_comment.outputs.COMMENT_FILE_WRITTEN == 'true' - with: - name: python-coverage-comment-action - path: python-coverage-comment-action.txt - - # Build our site on the 3 major OSes and check for Sphinx warnings - build-site: - timeout-minutes: 15 - needs: [lint] - strategy: - fail-fast: false - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.12"] - sphinx-version: [""] - include: - - os: ubuntu-latest - python-version: "3.9" - sphinx-version: "old" - # crash with myst parser - # will be restored when myst will be dropped from our documentation - # - os: ubuntu-latest - # python-version: "3.12" - # sphinx-version: "dev" - env: - SPHINX_VERSION: ${{ matrix.sphinx-version }} - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - # waiting for https://github.com/nikeee/setup-pandoc/pull/8 - # using 12rambau fork until then - - uses: 12rambau/setup-pandoc@test - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - - name: Install dependencies - shell: bash - run: ./tools/github_actions_install.sh doc - - name: Show installed versions - run: python -m pip list - - name: Build docs - run: sphinx-build -b html docs/ docs/_build/html --keep-going -w warnings.txt - - name: Check for unexpected Sphinx warnings - run: python tests/utils/check_warnings.py - - # Run local Lighthouse audit against built site - audit: - needs: [build-site] - strategy: - matrix: - os: [ubuntu-latest] - python-version: ["3.12"] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - - name: Install dependencies - run: ./tools/github_actions_install.sh doc - shell: bash - - name: Show installed versions - run: python -m pip list - # We want to run the audit on a simplified documentation build so that - # the audit results aren't affected by non-theme things like extensions. - # Here we copy over just the kitchen sink into an empty docs site with - # only the theme loaded so extensions don't play a role in audit scores. - - name: Copy kitchen sink to a tiny site and build it - run: | - mkdir audit/ - mkdir audit/site - cp -r docs/examples/kitchen-sink audit/site/kitchen-sink - printf "Test\n====\n\n.. toctree::\n\n kitchen-sink/index\n" > audit/site/index.rst - echo 'html_theme = "pydata_sphinx_theme"' > audit/site/conf.py - echo '.. toctree::\n :glob:\n\n *' >> audit/site/index.rst - sphinx-build audit/site audit/_build - # The lighthouse audit runs directly on the HTML files, no serving needed - - name: Audit with Lighthouse - uses: treosh/lighthouse-ci-action@v11 - with: - configPath: ".github/workflows/lighthouserc.json" - temporaryPublicStorage: true - uploadArtifacts: true - runs: 3 # Multiple runs to reduce variance - - # Generate a profile of the code and upload as an artifact - profile: - needs: [build-site, run-pytest] - strategy: - matrix: - os: [ubuntu-latest] - python-version: ["3.12"] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - cache: "pip" - cache-dependency-path: "pyproject.toml" - - name: Install dependencies - run: ./tools/github_actions_install.sh test nox - shell: bash - - name: Show installed versions - run: python -m pip list - - name: Generate a profile - run: nox -s profile - continue-on-error: true - - uses: actions/upload-artifact@v4 - with: - name: profile-results - path: profile.svg diff --git a/docs/community/setup.md b/docs/community/setup.md index 309e53ece..119d423ba 100644 --- a/docs/community/setup.md +++ b/docs/community/setup.md @@ -2,23 +2,25 @@ This section covers the simplest way to get started developing this theme locally so that you can contribute. It uses automation and as few steps as possible to get things done. -If you'd like to do more operations manually, see [](topics/manual-dev.md). + +If you are comfortable with and prefer a more manual setup refer to the [](topics/manual-dev.md) section. ## Workflow for contributing changes We follow a [typical GitHub workflow](https://guides.github.com/introduction/flow/) of: -- create a personal fork of this repo -- create a branch +- create a personal fork and local clone of this repo +- create a branch for your contribution - open a pull request - fix findings of various linters and checks - work through code review -For each pull request, the documentation is built and deployed to make it easier to review the changes in the PR. -To access this, click on the {{ rtd }} preview in the CI/CD jobs. +For each pull request (PR), the documentation is built and deployed to make it easier to review the changes in the PR. +To access this preview, click on the {{ rtd }} preview in the CI/CD jobs (GitHub checks section +at the bottom of a PR, note you might need to click on "Show all checks" to access the job). -The sections below cover the steps to take in more detail. +The sections below cover the contribution steps in more detail. ## Clone the repository @@ -36,32 +38,32 @@ You can clone it for local development like so: ## Install your tools -Building a Sphinx site uses a combination of Python and Jinja to manage HTML, SCSS, and JavaScript. +Building a Sphinx site uses a combination of Python and `Jinja` to manage `HTML`, `scss`, and `JavaScript`. To simplify this process, we use a few helper tools: - [The Sphinx Theme Builder](https://sphinx-theme-builder.readthedocs.io/en/latest/) compiles web assets in an automated way. - [pre-commit](https://pre-commit.com/) for automatically enforcing code standards and quality checks before commits. -- [nox](https://nox.thea.codes/) for automating common development tasks. +- [tox](https://tox.wiki/en/latest/index.html) for automating common development tasks. - [pandoc](https://pandoc.org/) the universal document converter. -In particular, `nox` can be used to automatically create isolated local development environments with all the correct packages installed to work on the theme. -The rest of this guide focuses on using `nox` to start with a basic environment. +In particular, `tox` can be used to automatically create isolated local development environments with all the correct packages installed to work on the theme. +The rest of this guide focuses on using `tox` to start with a basic environment. ```{seealso} The information on this page covers the basics to get you started, for information about manually compiling assets, see [](topics/manual-dev.md). ``` -### Setup `nox` +### Setup `tox` -To start, install `nox`: +To start, install `tox`: ```console -$ pip install nox +$ pip install tox ``` -You can call `nox` from the command line to perform common actions that are needed in building the theme. -`nox` operates with isolated environments, so each action has its own packages installed in a local directory (`.nox`). -For common development actions, you'll only need to use `nox` and won't need to set up any other packages. +You can call `tox` from the command line to perform common actions that are needed in building the theme. +`tox` operates with isolated environments, so each action has its packages installed in a local directory (`.tox`). +For common development actions, you'll only need to use `tox` and won't need to set up any other packages. ### Setup `pre-commit` @@ -82,24 +84,32 @@ $ pre-commit install This will install the necessary dependencies to run `pre-commit` every time you make a commit with Git. -:::{note} -Your `pre-commit` dependencies will be installed in the environment from which you're calling `pre-commit`, `nox`, etc. -They will not be installed in the isolated environments used by `nox`. -::: +```{note} +Your `pre-commit` dependencies will be installed in the environment from which you're calling `pre-commit`, `tox`, etc. +They will not be installed in the isolated environments used by `tox`. +``` + +Alternatively, if you do not want to install pre-commit and its dependencies globally, you can use `tox` to run the checks: + +```python +tox -e run lint +``` + +The caveat to using `tox` is that this will not install the required hooks to run the checks automatically before each commit, so you need to run this manually. ## Build the documentation -Now that you have `nox` installed and cloned the repository, you should be able to build the documentation locally. +Now that you have `tox` installed and cloned the repository, you should be able to build the documentation locally. -To build the documentation with `nox`, run the following command: +To build the documentation with `tox`, run the following command: ```console -$ nox -s docs +$ tox run -e docs-dev ``` This will install the necessary dependencies and build the documentation located in the `docs/` folder. -They will be placed in a `docs/_build/html` folder. -If the docs have already been built, it will only build new pages that have been updated. +The generated documentation will be placed in a `docs/_build/html` folder. +If the docs have already been built, it will only rebuild the pages that have been updated. You can open one of the HTML files there to preview the documentation locally. Alternatively, you can invoke the built-in Python [http.server](https://docs.python.org/3/library/http.server.html#module-http.server) with: @@ -115,36 +125,36 @@ This will print a local URL that you can open in a browser to explore the HTML f Now that you've built the documentation, edit one of the source files to see how the documentation updates with new builds. 1. **Make an edit to a page**. For example, add a word or fix a typo on any page. -2. **Rebuild the documentation** with `nox -s docs` +2. **Rebuild the documentation** with `tox run -e docs-dev` -It should go much faster this time because `nox` is re-using the previously created environment, and because Sphinx has cached the pages that you didn't change. +It should go much faster this time because `tox` is re-using the previously created environment, and because Sphinx has cached the pages that you didn't change. ## Compile the CSS/JS assets The source files for CSS and JS assets are in `src/pydata_sphinx_theme/assets`. These are then built and bundled with the theme (e.g., `scss` is turned into `css`). -To compile the CSS/JS assets with `nox`, run the following command: +To compile the CSS/JS assets with `tox`, run the following command: ```console -$ nox -s compile +$ tox run -e compile ``` This will compile all assets and place them in the appropriate folder to be used with documentation builds. ```{note} Compiled assets are **not committed to git**. -The `sphinx-theme-builder` will bundle these assets automatically when we make a new release, but we do not manually commit these compiled assets to git history. +The `sphinx-theme-builder` will bundle these assets automatically when we make a new release, but we do not manually commit these compiled assets to Git history. ``` ## Run a development server You can combine the above two actions (build the docs and compile JS/CSS assets) and run a development server so that changes to `src/` are automatically bundled with the package, and the documentation is immediately reloaded in a live preview window. -To run the development server with `nox`, run the following command: +To run the development server with `tox`, run the following command: ```console -$ nox -s docs-live +$ tox run -e docs-live ``` When working on the theme, making changes to any of these directories: @@ -152,6 +162,7 @@ When working on the theme, making changes to any of these directories: - `src/js/index.js` - `src/scss/index.scss` - `docs/**/*.rst` +- `docs/**/*.md` - `docs/**/*.py` will cause the development server to do the following: @@ -162,7 +173,7 @@ will cause the development server to do the following: ## Run the tests -This theme uses `pytest` for its testing. There is a lightweight fixture defined +This theme uses `pytest` and `playwright` for testing. There is a lightweight fixture defined in the `test_build.py` script that makes it straightforward to run a Sphinx build using this theme and inspect the results. There are also several automated accessibility checks in `test_a11y.py`. @@ -181,14 +192,30 @@ test will fail. If we _expect_ the structure to differ, then delete the file on disk and run the test. A new file will be created, and subsequent tests will pass. -To run the build tests with `nox`, run the following command: +To run the build tests with `tox`, run the following command: ```console -$ nox -s test +# this will compile the assets and run the tests (with test coverage) +# note the use of the `-m` flag vs. other commands in this guide +$ tox run -m tests + +# to run the tests only without pre-compiling the assets and without coverage (for example if you recently compiled the assets) +$ tox run -e tests-no-cov ``` To run the accessibility checks: ```console -$ nox -s a11y +# this will compile the assets, build the documentation, and run the accessibility tests +$ tox run -m a11y + +# to run the tests without pre-compiling the assets and without re-building the docs (for example if you recently compiled the assets or built the docs) +$ tox run -e a11y-tests ``` + +## GitHub Codespaces + +If you have good internet connectivity and want a temporary set-up, it is often faster to work on the PyData Sphinx Theme +in a Codespaces environment. +Once your Codespaces instance is set up, you can run the `tox` commands above to build the documentation, compile the assets, and run the tests. +For documentation on how to get started with Codespaces, see [the Codespaces documentation](https://docs.github.com/en/codespaces). diff --git a/docs/community/topics/accessibility.md b/docs/community/topics/accessibility.md index 4e4485d9c..957b4fcb5 100644 --- a/docs/community/topics/accessibility.md +++ b/docs/community/topics/accessibility.md @@ -6,8 +6,8 @@ April-2023: we are currently and reporting, so this may change soon. ``` -In general, accessibility-checking tools can find a limited number of common HTML patterns which -assistive technology can't help users understand. +In general, accessibility-checking tools can find a limited number of common HTML patterns that assistive technology +can't help users understand. ## Accessibility checks as part of our development process diff --git a/docs/community/topics/assets.md b/docs/community/topics/assets.md index c893c6348..848da625a 100644 --- a/docs/community/topics/assets.md +++ b/docs/community/topics/assets.md @@ -18,7 +18,7 @@ site after upgrading the theme. To compile the assets and bundle them with the theme, run this command: ```console -$ nox -s compile +$ tox -e run compile ``` ## Styles (SCSS) and Scripts (JS) diff --git a/docs/community/topics/manual-dev.md b/docs/community/topics/manual-dev.md index 2b05d5888..5c952633a 100644 --- a/docs/community/topics/manual-dev.md +++ b/docs/community/topics/manual-dev.md @@ -2,7 +2,8 @@ # Set up a manual development environment -If you prefer not to use automation tools like `nox`, or want to have more control over the specific version of packages that you'd like installed, you may also manually set up a development environment locally. +If you prefer not to use automation tools like `tox`, or want to have more control over the specific version of packages that you'd like installed, +you may set your local development environment manually. To do so, follow the instructions on this page. @@ -12,18 +13,12 @@ This is optional, but it's best to start with a fresh development environment so To do so, use a tool like [conda](https://docs.conda.io/en/latest/), [mamba](https://github.com/mamba-org/mamba), or [virtualenv](https://virtualenv.pypa.io/). -## Install dependencies +## Pre-requisites -You must install `sphinx-theme-builder` and Pandoc. +Before you start, ensure that you have the following installed: -We use the `sphinx-theme-builder` to install `nodejs` locally and to compile all CSS and JS assets needed for the theme. -Install it like so (note the `cli` option so that we can run it from the command line): - -```console -$ pip install "sphinx-theme-builder[cli]" -``` - -We use `nbsphinx` to support notebook (.ipynb) files in the documentation, which requires [installing Pandoc](https://pandoc.org/installing.html) at a system level (or within a Conda environment). +- Python >= 3.9 +- [Pandoc](https://pandoc.org/installing.html): we use `nbsphinx` to support notebook (.ipynb) files in the documentation, which requires [installing Pandoc](https://pandoc.org/installing.html) at a system level (or within a Conda environment). ## Clone the repository locally diff --git a/pyproject.toml b/pyproject.toml index 1856c9e21..d1532b6b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,9 +78,10 @@ doc = [ "colorama", "ipywidgets" ] -test = ["pytest", "pytest-cov", "pytest-regressions"] -dev = ["pyyaml", "pre-commit", "nox", "pydata-sphinx-theme[doc,test]"] +test = ["pytest", "pytest-cov", "pytest-regressions", "sphinx[test]"] +dev = ["pyyaml", "pre-commit", "pydata-sphinx-theme[doc,test]", "tox", "pandoc", "sphinx-theme-builder[cli]"] a11y = ["pytest-playwright"] +i18n = ["Babel", "jinja2"] [project.entry-points] "sphinx.html_themes" = { pydata_sphinx_theme = "pydata_sphinx_theme" } @@ -124,4 +125,9 @@ branch = true relative_files = true [tool.coverage.report] -fail_under = 60 +fail_under = 80 +show_missing = true +skip_covered = true + +[tool.coverage.paths] +source = ["src", ".tox/py*/**/site-packages"] diff --git a/tests/README.md b/tests/README.md index ea7c0196e..32d4017a2 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,6 +1,6 @@ # PyData Sphinx tests -This directory contains the Python tests for the theme. These tests are built with [pytest](https://docs.pytest.org/en/stable/) and are called through `nox`. +This directory contains the Python tests for the theme. These tests are built with [pytest](https://docs.pytest.org/en/stable/) and are called through `tox`. - `test_build.py` checks that the static HTML output of the build process conforms to various expectations. It builds static HTML pages based on configurations in @@ -18,7 +18,7 @@ This directory contains the Python tests for the theme. These tests are built wi In contrast to the build test suite, the accessibility suite checks components as they appear in the browser, meaning with any CSS and JavaScript applied. It does this by building the PyData Sphinx Theme docs, launching a local server to the - docs, then checking the "Kitchen Sink" example pages with + docs, and then checking the "Kitchen Sink" example pages with [Playwright](https://playwright.dev), a program for developers that allows loading and manipulating pages with various browsers, such as Chrome (chromium), Firefox (gecko), Safari (WebKit). @@ -27,17 +27,17 @@ The ["Kitchen Sink" examples](https://pydata-sphinx-theme.readthedocs.io/en/stab are taken from [sphinx-themes.org](https://sphinx-themes.org/) and showcase components of the PyData Sphinx Theme, such as admonitions, lists, and headings. -## visually debugging the test pages +## Visually debugging the test pages It can be useful to build and inspect the test pages in the browser. -By default `nox -s test` will build the html in a temporary directory. +By default, `tox run -m tests` (or any other `tox` command that runs our tests) will build the HTML in a temporary directory. You can change this by using the `PST_TEST_HTML_DIR` environment variable. For example: -``` -$ PST_TEST_HTML_DIR=./debug-test-theme/ nox -s test +```bash +$ PST_TEST_HTML_DIR=./debug-test-theme/ tox run -m tests ``` -Will save all the generated html in the folders `./debug-test-theme//` +Will save all the generated HTML in the folders `./debug-test-theme//` diff --git a/tools/github_actions_install.sh b/tools/github_actions_install.sh deleted file mode 100755 index 90e08f1b0..000000000 --- a/tools/github_actions_install.sh +++ /dev/null @@ -1,18 +0,0 @@ -#!/bin/bash - -# First arg ($1) is the extras_require to install, optional second arg -# ($2) is an extra dependency (currently just nox on one run) -set -eo pipefail -export PYTHONUTF8=1 -if [[ "$SPHINX_VERSION" == "rls" ]]; then - SPHINX_INSTALL="sphinx[test]" -elif [[ "$SPHINX_VERSION" == "dev" ]]; then - SPHINX_INSTALL="sphinx[test]@https://codeload.github.com/sphinx-doc/sphinx/zip/refs/heads/master" -elif [[ "$SPHINX_VERSION" == "old" ]]; then - SPHINX_INSTALL="sphinx[test]==6.1.0" -else # for the "build site" jobs - SPHINX_INSTALL="sphinx<7.3" -fi -set -x # print commands -python -m pip install --upgrade pip wheel setuptools -python -m pip install -e .["$1"] ${SPHINX_INSTALL} $2 diff --git a/tools/profile.py b/tools/profile.py new file mode 100644 index 000000000..8d5b3a50b --- /dev/null +++ b/tools/profile.py @@ -0,0 +1,99 @@ +"""Script to profile the build of the test site with py-spy. + +This can be called with `python tools/profile.py` and will profile the build of the test site. +You can additionally configure the number of extra pages to add to the build with the `-n` flag and the output file with the `-o` flag. + +$ python tools/profile.py -n 100 -o profile.svg + +If running within tox (recommended) this can be run with: + +$ tox -e profile-docs -- -n 100 -o profile.svg + +""" + +import argparse +import shutil as sh +import subprocess +import tempfile +from pathlib import Path +from textwrap import dedent + + +def profile_docs(output: str = "profile.svg", n_extra_pages: int = 50) -> None: + """Add a bunch of extra pages to the test site and profile the build with py-spy. + + Args: + output (str): The output filename for generated chart, defaults to output.svg. + n_extra_pages (int): The number of extra pages to add to the build, defaults to 50. + """ + # base path of the test site + base_site_path = Path("tests/sites/base") + + with tempfile.TemporaryDirectory() as tmpdir: + + # copy over the base test site to the temporary folder + target_path = Path(tmpdir) / base_site_path + sh.copytree(base_site_path, target_path) + print(f"Copied {base_site_path} to {target_path}") + + # Add a bunch of extra files to increase the build length + index_file = target_path / "index.rst" + dummy_text = index_file.read_text() + dummy_text += dedent( + """ + .. toctree:: + :glob: + + many/* + """ + ) + index_file.write_text(dummy_text) + many_files_path = target_path / "many" + try: + many_files_path.mkdir(parents=False) + except FileNotFoundError: + print(f"Could not create directory {many_files_path}") + + # create a bunch of empty pages to slow the build + for page in range(n_extra_pages): + (many_files_path / f"{page}.rst").write_text("Test\n====\n\nbody\n") + + # Output directory + output_site_path = target_path / "_build" + + # Profile the build + print(f"Profiling build with {n_extra_pages} pages with py-spy...") + + subprocess.run( + [ + "py-spy", + "record", + "-o", + output, + "--", + "sphinx-build", + target_path, + f" {output_site_path}/_build", + ], + capture_output=True, + ) + + print("py-spy profiler output at this file:", output) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument( + "-o", "--output", type=str, help="Output filename, the default is profile.svg" + ) + parser.add_argument( + "-n", "--n_pages", type=int, help="Number of extra pages to add to the build" + ) + + # Parse the arguments + args = parser.parse_args() + output_file = args.output if args.output else "profile.svg" + n_pages = args.n_pages if args.n_pages else 50 + + profile_docs(output=output_file, n_extra_pages=n_pages) diff --git a/tox.ini b/tox.ini new file mode 100644 index 000000000..6920e004b --- /dev/null +++ b/tox.ini @@ -0,0 +1,127 @@ +[tox] +min_version = 4 +# list of environments to run by default with tox run +# using Python 3.12 as the default, this needs to be kept in line with +# .github/actions/set-dev-env/action.yml (default Python version) +env_list = + lint, + compile, + py312-docs, + py312-tests, + a11y-tests + +# convenience label for running tests with a given Python version, aimed at +# helping contributors run tests locally +# tox run -m tests +labels = + tests = compile, py312-tests + a11y = compile, py312-docs, a11y-tests + +# general tox env configuration +# these can be run with any py3{9,12} version +# tox run -e py39-lint +[testenv] +deps = + lint: pre-commit + compile: sphinx-theme-builder[cli] + profile-docs: py-spy +extras = + {docs-no-checks, docs-linkcheck, profile-docs}: doc +skip_install = + lint: true # do not need to install to lint + profile-docs: true # avoids issues with py-spy and setting the git repo + compile: false +package = editable +commands = + lint: pre-commit run -a + compile: stb compile + # can substitute the target directory + docs-no-checks: sphinx-build {posargs:audit}/site {posargs:audit}/_build + docs-linkcheck: sphinx-build -W -b linkcheck docs/ docs/_build/html --keep-going + # example tox run -e py39-profile-docs -- -o profile.svg -n 100 + profile-docs: python ./tools/profile.py {posargs} + +# tests can be ran with or without coverage (see examples below), it is recommended to run compile before running tests (see examples below), +# tox run -e compile,py39-tests +# if you want to skip the assets compilation step you can run the tests without `compile` +# tox run -e py39-tests +# run tests with a specific Sphinx version +# tox run -e compile,py39-sphinx61-tests +# run tests without coverage +# tox run -e compile,py39-tests-no-cov +[testenv:py3{9,10,11,12}{,-sphinx61,-sphinxdev,}-tests{,-no-cov}] +description = "Run tests Python and Sphinx versions. If a Sphinx version is specified, it will use that version vs the default in pyproject.toml" +# need to ensure the package is installed in editable mode +package = editable +extras = + test # install dependencies - defined in pyproject.toml +deps = + coverage[toml] + py39-sphinx61-tests: sphinx~=6.1.0 + py312-sphinxdev: sphinx[test] @ git+https://github.com/sphinx-doc/sphinx.git@master +depends = compile +commands = + py3{9,10,11,12}{,-sphinx61,-sphinxdev,}-tests: coverage run -m pytest -m "not a11y" {posargs} + py3{9,10,11,12}{,-sphinx61,-sphinxdev,}-tests-no-cov: pytest -m "not a11y" {posargs} + +# run accessibility tests with Playwright and axe-core +# compiling the assets is needed before running the tests +# tox run -e compile,py312-docs,a11y-tests +[testenv:a11y-tests] +description = run accessibility tests with Playwright and axe-core +base_python = py312 # keep in sync with tests.yml +extras = + test + a11y +depends = + compile, + py312-docs +allowlist_externals=playwright +commands = + playwright install + pytest -m "a11y" {posargs} + +# build PST documentation with the default or a specific Sphinx version +# since we are building the documentation we install the packaged dev version of PST +# tox run -e py39-docs +# tox run -e py39-sphinx61-docs +[testenv:py3{9,12}{,-sphinx61}-docs] +description = build the documentation and place in docs/_build/html +# suppress Py3.11's new "can't debug frozen modules" warning +set_env = PYDEVD_DISABLE_FILE_VALIDATION=1 +# keep this in sync across all docs environments +extras = {[testenv:docs-no-checks]extras} +deps = + py39-sphinx61-docs: sphinx~=6.1.0 +commands = + sphinx-build -b html docs/ docs/_build/html -v -w warnings.txt {posargs} + python tests/utils/check_warnings.py + +# recommended for local development, this command will build the PST documentation +# with the default Sphinx version and the PST installed in editable mode +# tox run -e docs-dev +[testenv:docs-dev] +description = build the documentation and place in docs/_build/html (dev mode) +# suppress Py3.11's new "can't debug frozen modules" warning +set_env = PYDEVD_DISABLE_FILE_VALIDATION=1 +# keep this in sync across all docs environments +extras = {[testenv:docs-no-checks]extras} +package = editable +commands = + sphinx-build -b html docs/ docs/_build/html -v -w warnings.txt {posargs} + python tests/utils/check_warnings.py + + +[testenv:docs-live] +description = "Build and serve the documentation with live-reload" +extras = + dev + i18n +package = editable +deps = + sphinx-theme-builder[cli]@git+https://github.com/pradyunsg/sphinx-theme-builder#egg=d9f620b +# suppress Py3.11's new "can't debug frozen modules" warning +set_env = PYDEVD_DISABLE_FILE_VALIDATION=1 +commands = + pybabel compile -d src/pydata_sphinx_theme/locale -D sphinx + stb serve docs --open-browser --re-ignore=locale|api|_build|\.jupyterlite\.doit\.db \ No newline at end of file