diff --git a/.cspell.json b/.cspell.json index 9b804ae7..38ba3063 100644 --- a/.cspell.json +++ b/.cspell.json @@ -64,6 +64,8 @@ "nbsp", "nopython", "poethepoet", + "prek", + "prekignore", "prereleased", "pycode", "pygments", diff --git a/.github/workflows/autofix.ci.yml b/.github/workflows/autofix.ci.yml new file mode 100644 index 00000000..59a296e7 --- /dev/null +++ b/.github/workflows/autofix.ci.yml @@ -0,0 +1,58 @@ +# The workflow name is load-bearing: autofix.ci only accepts a patch that was +# uploaded from a workflow named "autofix.ci". Do not rename it. +name: autofix.ci + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: |- + ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }} + +on: + pull_request: + branches: + - main + - epic/* + - "[0-9]+.[0-9]+.x" + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + +env: + FORCE_COLOR: true + PREK_COLOR: always + +jobs: + autofix: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 # the Jupyter kernel update diffs against the base branch + - uses: j178/prek-action@4e14d07f9231acabce116ccfca13b13dd9755ece # v3.0.0 + with: + install-only: true + - id: prek + name: Run prek + continue-on-error: true + run: prek run --all-files + # The first run fails as soon as a hook rewrites a file, so it runs again + # against the fixed tree. A hook that only reports keeps the job red. + - if: steps.prek.outcome == 'failure' + name: Verify fixes + run: prek run --all-files + - if: >- + always() && !cancelled() && + github.event_name == 'pull_request' && + hashFiles('**/*.ipynb') + name: Update Jupyter kernels + uses: ComPWA/actions/update-jupyter-kernel@aa47e7f596961634fe54c446af2144a309501cda # PR #178 preview + with: + target-branch: origin/${{ github.event.pull_request.base.ref }} + upload-artifact: false + - if: always() && !cancelled() + name: Commit fixes + uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a # v1.3.4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8a77d1f9..16d67904 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,4 +30,4 @@ jobs: style: secrets: token: ${{ secrets.PAT }} - uses: ComPWA/actions/.github/workflows/style.yml@13f1a3b0c831615fb49c008bcc48113da8a76477 # 4.0.2 + uses: ComPWA/actions/.github/workflows/style.yml@aa47e7f596961634fe54c446af2144a309501cda # PR #178 preview diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dad8dfa4..3df15f56 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,14 +1,3 @@ -ci: - autofix_commit_msg: "MAINT: implement pre-commit autofixes" - autoupdate_commit_msg: "MAINT: upgrade lock files" - autoupdate_schedule: quarterly - skip: - - check-jsonschema - - tombi-format - - tombi-lint - - ty - - uv-lock - repos: - repo: meta hooks: @@ -16,7 +5,7 @@ repos: - id: check-useless-excludes - repo: https://github.com/ComPWA/policy - rev: 0.9.7 + rev: a7f74b709e14037368cfa311cf5a7c999ef7c946 # PR #695 preview hooks: - id: check-dev-files diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 373e0227..28e83048 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,16 +19,16 @@ uv sync --all-extras source .venv/bin/activate ``` -Formatting and linting checks are automatically performed when committing changes. This is done with [pre-commit](https://pre-commit.com). To install the hooks in your local repository, run install `pre-commit` with `uv`: +Formatting and linting checks are automatically performed when committing changes. This is done with [prek](https://prek.j178.dev), a drop-in replacement for [pre-commit](https://pre-commit.com) that reads the same `.pre-commit-config.yaml` file. To install the hooks in your local repository, [install `prek`](https://prek.j178.dev/installation) with `uv`: ```shell -uv tool install pre-commit --with pre-commit-uv --force-reinstall --python=3.13 +uv tool install prek --force-reinstall ``` -and [`pre-commit install`](https://pre-commit.com/#3-install-the-git-hook-scripts) **once**: +and run [`prek install`](https://prek.j178.dev/reference/cli/) **once**: ```shell -pre-commit install --install-hooks +prek install --prepare-hooks ``` [Poe the Poet](https://poethepoet.natn.io) is used as a task runner. Install it globally (within your home folder) with `uv`: diff --git a/README.md b/README.md index 0e57ee2f..51769aaf 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ [![Binder](https://static.mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/ComPWA/compwa.github.io/main) [![Google Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/ComPWA/compwa.github.io/blob/main) [![Open in Visual Studio Code](https://img.shields.io/badge/vscode-open-blue?logo=visualstudiocode)](https://open.vscode.dev/ComPWA/compwa.github.io) -[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/ComPWA/compwa.github.io/main.svg)](https://results.pre-commit.ci/latest/github/ComPWA/compwa.github.io/main) +[![prek](https://img.shields.io/badge/hooks-prek-261230.svg)](https://prek.j178.dev) [![Spelling checked](https://img.shields.io/badge/cspell-checked-brightgreen.svg)](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) diff --git a/docs/develop.md b/docs/develop.md index 68ef73e3..ef09ba42 100644 --- a/docs/develop.md +++ b/docs/develop.md @@ -30,13 +30,15 @@ pixi shell ::: :::: -In addition, [`pre-commit`](https://pre-commit.com) is used to enforce style checks and can be installed with:: +In addition, [prek](https://prek.j178.dev) is used to enforce style checks and can be installed with: ```shell -uv tool install --python=3.13 --with pre-commit-uv pre-commit -pre-commit install --install-hooks +uv tool install prek +prek install --prepare-hooks ``` +If you already had `pre-commit` installed in this repository, see {ref}`develop:Prek` for how to replace its Git hooks. + In `uv`-only projects, [Poe the Poet](https://poethepoet.natn.io) is used as task runner and can be installed with: ```shell @@ -187,7 +189,7 @@ requirements can be installed with the last example. ### Pinning dependency versions -To ensure that developers use exactly the same versions of the package dependencies and developer requirements, some of the repositories provide lock files, such as [`uv.lock`](https://docs.astral.sh/uv/concepts/projects/sync) and [`pixi.lock`](https://pixi.prefix.dev/v0.63.2/workspace/lockfile). The constraint files are updated automatically with the [ComPWA/actions/.github/workflows/lock.yml](https://github.com/ComPWA/actions/blob/v4/.github/workflows/lock.yml) workflow using {ref}`develop:GitHub Actions`. +To ensure that developers use exactly the same versions of the package dependencies and developer requirements, some of the repositories provide lock files, such as [`uv.lock`](https://docs.astral.sh/uv/concepts/projects/sync) and [`pixi.lock`](https://pixi.prefix.dev/v0.63.2/workspace/lockfile). The constraint files are updated automatically with the [ComPWA/actions/.github/workflows/lock.yml](https://github.com/ComPWA/actions/blob/v4/.github/workflows/lock.yml) workflow using {ref}`develop:GitHub Actions`. That workflow also bumps the hook revisions in `.pre-commit-config.yaml` with `uvx prek autoupdate`, which is what the {ref}`local ` `poe upgrade` task does as well. :::{note} Constraint files ensure that the framework is _deterministic and reproducible_ (up to @@ -315,42 +317,54 @@ notebooks with Julia kernels into your {ref}`documentation`, and when you make a {ref}`pull request `. +Where possible, we define and enforce our coding conventions through automated tools, instead of describing them in documentation. These tools perform their checks when you commit files locally (see {ref}`develop:Prek`), when {ref}`running checks locally `, and when you make a {ref}`pull request `. The tools are mainly configured through [`pyproject.toml`](https://github.com/ComPWA/ampform/blob/main/pyproject.toml) and the workflow files under [`.github`](https://github.com/ComPWA/ampform/blob/main/.github). These configuration files are kept up to date through the [ComPWA/policy](https://compwa.github.io/policy) repository, which essentially defines the developer environment across [all ComPWA repositories](https://github.com/orgs/ComPWA/repositories?q=archived%3Ano&type=all&language=&sort=name). If you run into persistent linting errors, this may mean we need to further specify our conventions. In that case, it's best to {ref}`create an issue ` or a {ref}`pull request ` at [ComPWA/policy](https://github.com/ComPWA/policy) and propose a policy change that can be formulated through those config files. -### Pre-commit +(pre-commit)= + +### Prek + +All {ref}`style checks ` are enforced through [prek](https://prek.j178.dev), a drop-in replacement for [pre-commit](https://pre-commit.com). It reads the same [{file}`.pre-commit-config.yaml`](https://github.com/ComPWA/ampform/blob/main/.pre-commit-config.yaml) file and the same hook definitions, so only the command and the local set-up differ. Install it once as a global tool with [`uv`](https://docs.astral.sh/uv): + +```shell +uv tool install prek +``` -All {ref}`style checks ` are enforced through a tool called -[{command}`pre-commit`](https://pre-commit.com). It's best to activate this tool locally -as well. This has to be done only once, after you clone the repository: +It's best to activate the hooks locally as well. This has to be done only once, after you clone the repository: ```shell -pre-commit install --install-hooks +prek install --prepare-hooks ``` -:::{margin} Initializing pre-commit -The first time you run {command}`pre-commit` after installing or updating its checks, it -may take some time to initialize. +:::{margin} Initializing prek +The `--prepare-hooks` flag installs the hook environments right away, instead of on your first commit. This may take some time, but prek creates the environments with `uv` and in parallel, so it is considerably faster than `pre-commit` was. ::: -Upon committing, {command}`pre-commit` runs a set of checks as defined in the file -[{file}`.pre-commit-config.yaml`](https://github.com/ComPWA/ampform/blob/main/.pre-commit-config.yaml) -over all staged files. You can also quickly run all checks over _all_ indexed files in -the repository with the command: +Upon committing, prek runs a set of checks as defined in the file [{file}`.pre-commit-config.yaml`](https://github.com/ComPWA/ampform/blob/main/.pre-commit-config.yaml) over all staged files. You can also quickly run all checks over _all_ indexed files in the repository with the command: ```shell -pre-commit run -a +prek run --all-files ``` -Whenever you {ref}`submit a pull request `, this command is -automatically run -[on GitHub actions](https://github.com/ComPWA/ampform/actions/workflows/ci.yml) -and [on pre-commit.ci](https://results.pre-commit.ci/install/github/18435973) , ensuring -that all files in the repository follow the same conventions as set in the config files -of these tools. +:::{admonition} Coming from `pre-commit` +:class: tip +The Git hooks under `.git/hooks` are installed per developer and are not tracked by Git, so no policy update can replace them for you. If you previously ran `pre-commit install`, the shims there still call `pre-commit`. Either run `prek install --prepare-hooks` again to overwrite them, or let [`policy migrate`](https://compwa.github.io/policy/check-dev-files/migrations.html) detect and replace them: + +```shell +uvx --from git+https://github.com/ComPWA/policy --refresh policy migrate +``` + +You can then uninstall the old tool with `uv tool uninstall pre-commit`. The `pre-commit-uv` package is no longer needed either: prek uses `uv` natively. +::: + +:::{warning} +prek has a [workspace mode](https://prek.j178.dev/workspace/) that discovers _nested_ `.pre-commit-config.yaml` files, which `pre-commit` does not. If a repository ships such a file as a test fixture, list its directory in a [`.prekignore`](https://prek.j178.dev/workspace/) file, so that prek does not run it as a real project. +::: + +Whenever you {ref}`submit a pull request `, the same checks are run automatically [on GitHub Actions](https://github.com/ComPWA/ampform/actions/workflows/ci.yml), ensuring that all files in the repository follow the same conventions as set in the config files of these tools. Fixes that the hooks make are committed back to your branch by {ref}`autofix.ci `. ### Checks @@ -368,6 +382,38 @@ Poe the Poet is installed through the `dev` dependency group. However, since the uv tool install poethepoet ``` +The task that runs all {ref}`style checks ` is `style`. It is a thin wrapper around `prek run --all-files`, so these three commands are equivalent: + +::::{tab-set} +:::{tab-item} uv + +```shell +poe style +``` + +::: +:::{tab-item} Pixi + +```shell +pixi run style +``` + +::: +:::{tab-item} prek + +```shell +prek run --all-files +``` + +::: +:::: + +Repositories with lock files additionally define an `upgrade` task, which runs `prek autoupdate -j8` through the `_upgrade-prek` helper task, next to the helper tasks that upgrade the lock files themselves (see {ref}`develop:Pinning dependency versions`): + +```shell +poe upgrade +``` + ### GitHub Actions All {ref}`style checks `, testing of the @@ -378,6 +424,18 @@ All {ref}`style checks `, testing of the [`.github`](https://github.com/ComPWA/ampform/blob/main/.github) folder. All checks performed for each PR have to pass before the PR can be merged. +#### autofix.ci + +Most {ref}`style checks ` do not just report a problem, they fix it. Those fixes are committed back to your pull request by [autofix.ci](https://autofix.ci), a GitHub App that ComPWA repositories activate through a workflow file named {file}`.github/workflows/autofix.ci.yml`. The workflow runs `prek run --all-files`, updates the {ref}`Jupyter kernel names `, and hands the resulting patch to the app, which pushes it as a commit authored by `autofix-ci[bot]`. This replaces [pre-commit.ci](https://pre-commit.ci), which ComPWA repositories used before and which runs `pre-commit` rather than prek. + +There are a few things to be aware of: + +- The workflow file has to be named exactly `autofix.ci`. The service uses that name to recognize the workflow it trusts, both in the action and on its own servers, so renaming the file silently disables the fixes. +- autofix.ci does apply fixes to pull requests from forks, which the previous push job could not do. It does _not_ apply a patch when the last four commits were authored by a bot, so a long series of automated commits has to be interrupted by a human commit. +- Since autofix.ci commits the fixes, the {ref}`style workflow ` skips its own push job when it finds an `autofix.ci.yml` in the repository. Fixes are therefore never committed twice. + +If a check only _reports_ a problem, such as a spelling mistake or a type error, there is nothing to commit and you have to fix it yourself. Run `poe style` locally to see the same output. + ## Style checks ### Formatting @@ -394,7 +452,7 @@ Ruff](https://beta.ruff.rs/docs/rules/#isort-i)). For other code, we use they offer only limited configuration options, as to make formatting as conform as possible. -{ref}`develop:Pre-commit` performs some additional formatting jobs. For instance, it +{ref}`develop:Prek` performs some additional formatting jobs. For instance, it formats Jupyter notebooks with [nbQA](https://github.com/nbQA-dev/nbQA) and strips them of any output cells with [`nbstripout`](https://github.com/kynan/nbstripout). @@ -416,7 +474,7 @@ As a tool, we use [cSpell](https://github.com/streetsidesoftware/cspell/blob/master/packages/cspell/README.md), because it allows to check variable names in camel case and snake case. This way, a spelling checker helps you avoid mistakes in the code as well! cSpell is enforced -through pre-commit. +through {ref}`prek `. Accepted words are tracked through the [`.cspell.json`](https://github.com/ComPWA/ampform/blob/main/.cspell.json) file. As with @@ -558,7 +616,7 @@ poe docnblive ``` :::{tip} -Notebooks are automatically formatted through {ref}`pre-commit ` (see {ref}`develop:Formatting`). If you want to format the notebooks automatically as you're working, you can do so with [`jupyterlab-code-formatter`](https://jupyterlab-code-formatter.readthedocs.io), which is automatically {ref}`installed with the dev requirements `. +Notebooks are automatically formatted through {ref}`prek ` (see {ref}`develop:Formatting`). If you want to format the notebooks automatically as you're working, you can do so with [`jupyterlab-code-formatter`](https://jupyterlab-code-formatter.readthedocs.io), which is automatically {ref}`installed with the dev requirements `. For this, you need to set Ruff as the formatter (see [](#formatting)) for `jupyterlab-code-formatter`: diff --git a/pyproject.toml b/pyproject.toml index 41d7cc3c..0c4bd3d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -197,17 +197,20 @@ sequence = [ ] [tool.poe.tasks.style] -cmd = "pre-commit run --all-files" +cmd = "prek run --all-files" executor = { group = "style" } help = "Perform all linting, formatting, and spelling checks" [tool.poe.tasks.upgrade] executor = { type = "simple" } help = "Upgrade lock files" -parallel = ["_upgrade-precommit", "_upgrade-uv"] +parallel = [ + "_upgrade-prek", + "_upgrade-uv", +] -[tool.poe.tasks._upgrade-precommit] -cmd = "pre-commit autoupdate -j8" +[tool.poe.tasks._upgrade-prek] +cmd = "prek autoupdate -j8" executor = { type = "simple" } [tool.poe.tasks._upgrade-uv] @@ -421,7 +424,7 @@ key-empty = "off" [[tool.tombi.schemas]] root = "tool.compwa.policy" -path = "https://raw.githubusercontent.com/ComPWA/policy/0.9.7/compwa-policy.schema.json" +path = "https://raw.githubusercontent.com/ComPWA/policy/a7f74b709e14037368cfa311cf5a7c999ef7c946/compwa-policy.schema.json" include = ["pyproject.toml"] [[tool.ty.overrides]]