diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 04e55e4..5154d85 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,15 +9,13 @@ on: jobs: pre-commit: runs-on: ubuntu-latest - name: standardize thyself + name: Standardize thyself steps: - uses: actions/checkout@v6 - - run: python -m pip install pre-commit - shell: bash - - run: python -m pip freeze --local - shell: bash - - run: pre-commit run --show-diff-on-failure --color=always - shell: bash + - uses: actions/setup-python@v6 + with: + python-version: '3.10' + - uses: pre-commit/action@v3.0.1 pytest: runs-on: ubuntu-latest @@ -27,6 +25,19 @@ jobs: - run: uv sync - run: uv run pytest + readme-version: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - name: Check README rev matches pyproject.toml version + run: | + VERSION=$(grep '^version' pyproject.toml | sed 's/version = "\(.*\)"/\1/') + grep -q "rev: v$VERSION" README.md || { + echo "README.md rev does not match pyproject.toml version v$VERSION" + echo "Run 'just sync-readme' to fix." + exit 1 + } + tag-release: runs-on: ubuntu-latest needs: [pre-commit, pytest] diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..c8cfe39 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.10 diff --git a/DEVELOPING.md b/DEVELOPING.md index 1cd9e67..5ec520c 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -14,6 +14,15 @@ Releases follow semantic versioning: - **Minor** -- new checks, formatting changes, or new linting checks. May require fixing existing code. - **Major** -- removed checks or other breaking changes to existing API +To bump the version, use `just bump` -- it updates `pyproject.toml` and syncs the README pin in one step: + +```shell +just bump minor # or major / patch +``` + +CI will fail on PRs where the two are out of sync. +The tag is pushed automatically by CI on merge to main. + ## Testing Add files to `test_files/` to validate linter settings work, if other files of that type are not present in this repo. diff --git a/Justfile b/Justfile index 8864756..d38f24d 100644 --- a/Justfile +++ b/Justfile @@ -1,3 +1,19 @@ verify: pre-commit clean pre-commit run --all-files + +# Update the rev: pin in README.md to match the current package version. +sync-readme: + #!/usr/bin/env bash + set -euo pipefail + VERSION=$(uv version --short) + sed -i "s/rev: v[0-9]*\.[0-9]*\.[0-9]*/rev: v$VERSION/" README.md + echo "README.md pinned to v$VERSION" + +# Bump the package version and sync the README pin in one step. +# Usage: just bump minor (or major / patch) +bump level: + #!/usr/bin/env bash + set -euo pipefail + uv version --bump {{ level }} + just sync-readme diff --git a/README.md b/README.md index dc4cd62..79553bc 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,21 @@ # Polymath Source Code Standard -Pre-commit hooks that enforce the Polymath Robotics Engineering formatting and linting standard for a variety of languages. - -This is a low-configuration, highly opinionated set of hooks that take the guesswork out of formatting. +[![CI](https://github.com/polymathrobotics/polymath_code_standard/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/polymathrobotics/polymath_code_standard/actions/workflows/test.yml) -One check is provided per file type, with all necessary settings bundled within the hook. +Pre-commit hooks that enforce the Polymath Robotics Engineering formatting and linting standard for a variety of languages. +This is a low-configuration, opinionated set of hooks that take the guesswork out of formatting. +One hook is provided per file type, with all necessary settings bundled. Consuming repositories reference this repo directly via `.pre-commit-config.yaml` -- no config files need to be copied or kept in sync. -See [DEVELOPING.md](./DEVELOPING.md) for the development workflows to make updates to this tool. - -# Usage +See [DEVELOPING.md](./DEVELOPING.md) for development workflows. ## Prerequisites > [!NOTE] -> You may want to add the following text from this prerequisites section to your own repository's `README.md`! +> Consider adding this prerequisites section to your own repository's `README.md`. Install [pre-commit](https://pre-commit.com). -While there are several ways to do this, our favorit is with [uv](https://github.com/astral-sh/uv) - it's "scary fast". +Our recommended approach is with [uv](https://github.com/astral-sh/uv). ```shell uv tool install --with pre-commit-uv pre-commit @@ -29,24 +27,23 @@ Set up pre-commit hooks in the repository: pre-commit install ``` -### Hooks +## Configuration -In your repository's `.pre-commit-config.yaml`, use these hooks. -See the following for a list of all available hooks. -Feel free to use only the ones that apply to your usage. +Add the following to your repository's `.pre-commit-config.yaml`. +Use only the hooks that apply to your project. ```yaml --- repos: - repo: https://github.com/polymathrobotics/polymath_code_standard - rev: v2.2.0 + rev: v2.3.1 hooks: - # Basic checks and fixes that apply to any text file and the git repository itself + # File hygiene for all staged files - id: polymath-general - # Enforce and insert copyright headers in source code for the project's license + # Copyright headers and LICENSE file management - id: polymath-copyright - args: [--license, , --copyright-org, ] - # Specific languages + args: [--license, Apache-2.0, --copyright-org, "Your Org Name"] + # Language-specific checks - id: polymath-python - id: polymath-cpp - id: polymath-ros @@ -58,35 +55,216 @@ repos: - id: polymath-yaml - id: polymath-toml - id: polymath-json + - id: polymath-ansible ``` +See the [Hook Reference](#hook-reference) for details on each hook and its available arguments. + ## First-time use -Apply your newly configured hooks to all sources with the following. -You should also do this whenever you update to a newer version. +Apply your newly configured hooks to all existing files: ```shell pre-commit run --all-files ``` -You may now want to stage the new changes, then run again to check for any failures that require manual correction. +Stage the reformatted files, then run again to surface any failures that require manual correction. > [!NOTE] -> These formatters are likely not compatible with other formatting standards, for example in ROS you will now want to remove `ament_lint` in favor of these hooks. +> These formatters are not compatible with other formatting standards. +> In ROS projects, remove `ament_lint` in favor of these hooks. > [!NOTE] -> After a large reformatting pass, add the commit hash to `.git-blame-ignore-revs` so that `git blame` points back to the original authors rather than the reformatting commit. +> After a large reformatting pass, add the commit hash to `.git-blame-ignore-revs` so `git blame` points back to the original authors rather than the reformatting commit. ## CI -See [.github/workflows/test.yml](./.github/workflows/test.yml) for a simple GitHub Actions configuration that runs pre-commit hooks. +Add the following GitHub Actions workflow to run pre-commit on every push and pull request: + +```yaml +--- +name: Lint + +on: + push: + branches: [main] + pull_request: + +jobs: + pre-commit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.10' + - uses: pre-commit/action@v3.0.1 +``` + +## Hook Reference + +### `polymath-general` + +Applies file hygiene checks to all staged files. + +- Prevents committing large files +- Detects filename case conflicts +- Detects merge conflict markers +- Ensures shebanged scripts are executable +- Validates symlinks +- Blocks git submodules +- Adds a trailing newline to files +- Normalizes line endings +- Removes trailing whitespace + +No arguments. + +--- + +### `polymath-copyright` + +Inserts and validates copyright headers for Python, CMake, Shell, C, and C++ files. +Also creates or updates the `LICENSE` file (skipped for proprietary licenses). +Python, CMake, and Shell files use `#` comment style. +C and C++ files use `//` comment style. + +**Required:** + +- `--license SPDX_ID` -- SPDX license ID (e.g. `Apache-2.0`, `MIT`) or `proprietary` +- `--copyright-org ORG` -- Name of the copyright-holding organization (mutually exclusive with `--wildcard-copyright-org`) +- `--wildcard-copyright-org` -- Accept any copyright holder on the copyright line, for multi-contributor repos (mutually exclusive with `--copyright-org`) + +**Optional:** + +- `--copyright-year YEAR` -- Copyright start year (default: current year) +- `--reuse-style` -- Force REUSE-style 2-line copyright headers +- `--relicense` -- Strip any existing leading comment block before inserting the new header + +Example: + +```yaml +- id: polymath-copyright + args: [--license, Apache-2.0, --copyright-org, "Polymath Robotics, Inc."] +``` + +--- + +### `polymath-python` + +Runs `ruff format`, `ruff check --fix`, and Python AST validation. + +> [!NOTE] +> This hook writes `/.ruff.toml` to the consuming repo root. +> Add `/.ruff.toml` to `.gitignore`. +> See [`.ruff.toml` note](#rufftoml-is-written-to-the-consuming-repo) for details. + +No arguments. + +--- + +### `polymath-cpp` + +Runs `clang-format` and `cpplint` on C and C++ files using Polymath's bundled configuration. + +No arguments. + +--- + +### `polymath-ros` + +Enforces ROS-specific C++ conventions. +Requires that multi-threaded executors (`MultiThreadedExecutor`, `EventsCBGExecutor`) specify an explicit thread count. +Suppress a check on a specific line with a trailing `// NOLINT` comment. + +No arguments. + +--- + +### `polymath-shell` + +Runs `shellcheck` on shell scripts. +Detects scripts by shebang line, not just file extension. +Excludes `.envrc` files. + +No arguments. + +--- + +### `polymath-cmake` + +Runs `cmakelint` on CMake files with a maximum line length of 140. + +No arguments. + +--- + +### `polymath-docker` + +Runs `hadolint` on Dockerfiles. + +No arguments. + +--- + +### `polymath-markdown` + +Runs `pymarkdown` with the line-length rule disabled and auto-fixes issues where possible. + +No arguments. + +--- + +### `polymath-xml` + +Validates XML well-formedness and schema compliance. +Supports `xml-model` processing instructions and `xsi:noNamespaceSchemaLocation`. +Includes a bundled `package_format3.xsd` schema for ROS `package.xml` validation. -## NOTE: `.ruff.toml` is written to the consuming repo +No arguments. + +--- + +### `polymath-yaml` + +Formats and validates YAML files using `yamlfix`. +By default, adds a `---` explicit document start marker. + +**Optional:** + +- `--no-explicit-start` -- Omit the `---` document start marker + +--- + +### `polymath-toml` + +Validates TOML syntax. + +No arguments. + +--- + +### `polymath-json` + +Validates JSON and JSON5 syntax. +Excludes `.geojson` files. + +No arguments. + +--- + +### `polymath-ansible` + +Installs Ansible collections and roles from `ansible/requirements.yml` and runs `ansible-lint` with Polymath's configuration. +Dependencies are cached in `.polymath-ansible/` (automatically gitignored) and only reinstalled when `requirements.yml` changes. + +No arguments. + +--- -While `ruff` can take a `--config` argument to an absolute file, we are currently allowing subdirectories of a repository to override Ruff configuration. +## `.ruff.toml` is written to the consuming repo -To enable this, we have to omit `--config` and let Ruff walk up the directory tree. -This means we need to install our baseline `.ruff.toml` configuration in the root of the repo for Ruff to find. -Because `pre-commit` can run the same hook in parallel on batches of files, there is a race condition if we try to clean up that file after running. +While `ruff` can take a `--config` argument to an absolute file, subdirectory overrides require Ruff to walk up the directory tree. +To support this, the baseline `.ruff.toml` is installed in the repo root for Ruff to find. +Because pre-commit can run the same hook in parallel on batches of files, cleaning up that file after running would introduce a race condition. -TL;DR Add `/.ruff.toml` to `.gitignore` for the repository to ignore that it's been put there. +Add `/.ruff.toml` to `.gitignore` in the consuming repository. diff --git a/pyproject.toml b/pyproject.toml index 6ae2ba5..c5ec1bd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "polymath-code-standard" -version = "2.3.0" +version = "2.3.1" description = "Polymath Code Standard pre-commit hooks" requires-python = ">=3.10" dependencies = [ diff --git a/uv.lock b/uv.lock index b24e860..1435f5d 100644 --- a/uv.lock +++ b/uv.lock @@ -801,7 +801,7 @@ wheels = [ [[package]] name = "polymath-code-standard" -version = "2.3.0" +version = "2.3.1" source = { editable = "." } dependencies = [ { name = "ansible-lint" },