diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml deleted file mode 100644 index 01484a25..00000000 --- a/.github/workflows/autofix.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Auto-fix -on: - push: - branches: - 'main' - pull_request: - branches: - 'main' -jobs: - pre-commit: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: '3.11' - - name: Install pre-commit - run: pip install pre-commit - - name: Run pre-commit hooks - run: pre-commit run --all-files - - name: Apply automatic fixes using pre-commit-ci-lite - if: failure() && github.event_name == 'pull_request' - uses: pre-commit-ci/lite-action@v1.1.0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 495da7e2..7a28aa4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,31 +7,30 @@ on: workflow_dispatch: jobs: - test: + test-smoke: + name: Smoke tests (${{ matrix.os }}, ${{ matrix.python-version }}) + if: github.event_name == 'pull_request' || github.event_name == 'push' strategy: fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14', 'pypy-3.9-v7.x'] - os: [ubuntu-latest, windows-latest, macos-latest] include: - - python-version: pypy-3.9-v7.x + - os: ubuntu-latest + python-version: '3.8' + test-script: test:test + - os: ubuntu-latest + python-version: '3.14' + test-script: test:with-coverage + upload-coverage: true + - os: ubuntu-latest + python-version: pypy-3.9-v7.x py: pypy3 - # Just to slim down the test matrix: - exclude: - - python-version: '3.9' - os: macos-latest - - python-version: '3.9' - os: windows-latest - - python-version: '3.10' - os: ubuntu-latest - - python-version: '3.11' - os: macos-latest - - python-version: '3.11' - os: windows-latest - # PyPy 3.9 on Windows fails to build zstandard (hatch dep) due to - # missing distutils.msvc9compiler in cffi on PyPy; no value in testing this combo - - python-version: 'pypy-3.9-v7.x' - os: windows-latest + test-script: test:test + - os: macos-latest + python-version: '3.14' + test-script: test:test + - os: windows-latest + python-version: '3.14' + test-script: test:test runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v6 @@ -44,20 +43,54 @@ jobs: python -m pip install --upgrade hatch - name: Run tests run: | - hatch run +py=${{ matrix.py || matrix.python-version }} test:with-coverage + hatch run +py=${{ matrix.py || matrix.python-version }} ${{ matrix.test-script }} - name: Run integration tests run: | hatch run +py=${{ matrix.py || matrix.python-version }} integration:test shell: bash - name: Upload Codecov Results - if: success() + if: success() && matrix.upload-coverage uses: codecov/codecov-action@v6 with: file: ./coverage.xml flags: unittests - name: ${{ matrix.os }}/${{ matrix.python-version }} + name: smoke/${{ matrix.os }}/${{ matrix.python-version }} fail_ci_if_error: false + test-full: + name: Full tests (ubuntu, ${{ matrix.python-version }}) + if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && github.ref == 'refs/heads/main') + strategy: + fail-fast: false + matrix: + include: + - python-version: '3.8' + - python-version: '3.9' + - python-version: '3.10' + - python-version: '3.11' + - python-version: '3.12' + - python-version: '3.13' + - python-version: '3.14' + - python-version: pypy-3.9-v7.x + py: pypy3 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup Python ${{ matrix.python-version }} + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade hatch + - name: Run tests + run: | + hatch run +py=${{ matrix.py || matrix.python-version }} test:test + - name: Run integration tests + run: | + hatch run +py=${{ matrix.py || matrix.python-version }} integration:test + shell: bash + lint: runs-on: ubuntu-latest steps: @@ -68,26 +101,17 @@ jobs: python-version: '3.11' - name: Install Python dependencies run: | - python -m pip install --upgrade hatch + python -m pip install --upgrade hatch pre-commit - name: Setup Node uses: actions/setup-node@v6 with: node-version: 20 - - name: Check with ruff + - name: Run repository checks if: always() - run: hatch run style:lint + run: pre-commit run --all-files --show-diff-on-failure - name: Check with mypy if: always() run: hatch run types:check - - name: Check Markdown style - if: always() - run: hatch run lint:markdown - - name: Check JS style - if: always() - run: hatch run lint:js - - name: Check spelling - if: always() - run: hatch run lint:spelling package: runs-on: ubuntu-latest diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..7407f3cb --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,55 @@ +name: Security +on: + pull_request: + push: + branches: + - main + schedule: + - cron: '30 6 * * 1' + workflow_dispatch: + +permissions: + actions: read + contents: read + security-events: write + +jobs: + pip-audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Setup Python + uses: actions/setup-python@v6 + with: + python-version: '3.11' + - name: Audit Python dependencies + uses: pypa/gh-action-pip-audit@v1.1.0 + with: + inputs: . + summary: true + + osv-pr: + if: github.event_name == 'pull_request' + permissions: + actions: read + contents: read + security-events: write + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@v2.3.5 + with: + scan-args: |- + --include-git-root + --recursive + ./ + + osv-full: + if: github.event_name != 'pull_request' + permissions: + actions: read + contents: read + security-events: write + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v2.3.5 + with: + scan-args: |- + --include-git-root + --recursive + ./ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b95ce700..90033bfa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.11 hooks: - - id: ruff + - id: ruff-check args: [--fix] - id: ruff-format @@ -30,3 +30,16 @@ repos: - id: check-toml - id: check-merge-conflict - id: mixed-line-ending + + - repo: local + hooks: + - id: markdownlint + name: markdownlint + entry: npm exec --yes -- markdownlint-cli + language: system + files: ^(README\.md|CONTRIBUTING\.md|docs/.*\.md)$ + - id: jshint + name: jshint + entry: npm exec --yes -- jshint + language: system + files: ^mkdocs/.*\.js$ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d0d5f846..b1fb4703 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -54,17 +54,23 @@ The main tool that is used for development is [Hatch]. It manages dependencies ( So first, [install it][install Hatch]. Ideally in an isolated way with **`pipx install hatch`** (after [installing `pipx`]), or just `pip install hatch` as a more well-known way. -## Running all checks +For repository hooks, install [pre-commit] as well. Ideally use **`pipx install pre-commit`**, or `pip install pre-commit` if you prefer. The Markdown and JavaScript hooks also require Node.js to be available on your `PATH`. -To run **all** checks that are required for MkDocs, just run the following command in the cloned MkDocs repository: +## Running repository hooks + +To run the same repository-wide hooks as CI, use the following command in the cloned MkDocs repository: ```bash -hatch run all +pre-commit run --all-files ``` -**This will encompass all of the checks mentioned below.** +This runs the hooks defined in `.pre-commit-config.yaml`, including Python formatting and linting, spelling, YAML/TOML validation, Markdown linting, and JavaScript linting. -All checks need to pass. +If you want these hooks to run automatically on each commit, install them into your local Git checkout: + +```bash +pre-commit install +``` ### Running tests @@ -81,12 +87,12 @@ will be verified by [GitHub Actions] when you submit a pull request. ### Python code style -Python code within MkDocs' code base is formatted using [Black] and [Isort] and lint-checked using [Ruff], all of which are configured in `pyproject.toml`. +Python code within MkDocs' code base is formatted using Ruff's formatter and [Isort] and lint-checked using [Ruff]. The shared repository hooks live in `.pre-commit-config.yaml`, and GitHub Actions runs those hooks directly to avoid drift. -You can automatically check and format the code according to these tools with the following command: +You can check and automatically format the repository according to these tools with the following command: ```bash -hatch run style:fix +pre-commit run --all-files ``` The code is also type-checked using [mypy] - also configured in `pyproject.toml`, it can be run like this: @@ -95,14 +101,6 @@ The code is also type-checked using [mypy] - also configured in `pyproject.toml` hatch run types:check ``` -### Other style checks - -There are several other checks, such as spelling and JS style. To run all of them, use this command: - -```bash -hatch run lint:check -``` - ### Documentation of MkDocs itself After making edits to files under the `docs/` dir, you can preview the site locally using the following command: @@ -113,10 +111,10 @@ hatch run docs:serve Note that any 'WARNING' should be resolved before submitting a contribution. -Documentation files are also checked by markdownlint, so you should run this as well: +Documentation files are also checked by the repository hooks, so you should run this as well: ```bash -hatch run lint:check +pre-commit run --all-files ``` If you add a new plugin to mkdocs.yml, you don't need to add it to any "requirements" file, because that is managed automatically. @@ -181,12 +179,12 @@ rooms, and mailing lists is expected to follow the [PyPA Code of Conduct]. [virtualenv]: https://virtualenv.pypa.io/en/latest/user_guide.html [Hatch]: https://hatch.pypa.io/ [install Hatch]: https://hatch.pypa.io/latest/install/#pip +[pre-commit]: https://pre-commit.com/ [installing `pipx`]: https://pypa.github.io/pipx/installation/ [GitHub Actions]: https://docs.github.com/actions [PyPA Code of Conduct]: https://www.pypa.io/en/latest/code-of-conduct/ [Translating Themes]: https://mkdocs-ng.github.io/mkdocs/dev-guide/translations/ [Jinja's i18n extension]: https://jinja.palletsprojects.com/en/latest/extensions/#i18n-extension [Ruff]: https://docs.astral.sh/ruff/ -[Black]: https://black.readthedocs.io/ [Isort]: https://pycqa.github.io/isort/ [mypy]: https://mypy-lang.org/ diff --git a/pyproject.toml b/pyproject.toml index dee77dfe..e2e0fc70 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,15 +108,6 @@ dependencies = [ [tool.hatch.env] requires = ["hatch-mkdocs"] -[tool.hatch.envs.default.scripts] -all = [ - "hatch run style:fix", - "hatch run types:check", - "hatch run test:test", - "hatch run lint:check", - "hatch run +type=default integration:test", -] - [tool.hatch.envs.test] features = ["i18n"] dependencies = [ @@ -168,42 +159,6 @@ dependencies = [ [tool.hatch.envs.types.scripts] check = "mypy mkdocs" -[tool.hatch.envs.style] -detached = true -dependencies = [ - "black", - "isort", - "ruff", -] -[tool.hatch.envs.style.scripts] -check = [ - "isort --check-only --diff mkdocs docs", - "black -q --check --diff mkdocs docs", - "lint", -] -lint = [ - "ruff check mkdocs docs {args}" -] -fix = [ - "lint --fix", - "format", -] -format = [ - "isort -q mkdocs docs", - "black -q mkdocs docs", -] - -[tool.hatch.envs.lint] -detached = true -dependencies = [ - "codespell==2.2.6", -] -[tool.hatch.envs.lint.scripts] -spelling = 'codespell mkdocs docs *.* -S LC_MESSAGES -S "*.min.js" -S "lunr*.js" -S fontawesome-webfont.svg -S tinyseg.js -S "*.map"' -markdown = "npm exec --yes -- markdownlint-cli README.md CONTRIBUTING.md docs/ --ignore docs/CNAME" -js = "npm exec --yes -- jshint mkdocs/" -check = ["markdown", "js", "css", "spelling"] - [tool.hatch.env.collectors.mkdocs.docs] path = "mkdocs.yml" [tool.hatch.envs.docs] @@ -213,10 +168,6 @@ build = "mkdocs build" serve = "mkdocs serve" deploy = "mkdocs gh-deploy --force" -[tool.black] -line-length = 100 -skip-string-normalization = true - [tool.isort] profile = "black" line_length = 100