ferroclass 0.12.0

Hierarchical inventory management tool (reclass compatible)
Documentation
<!-- SPDX-FileCopyrightText: 2026 Michael Jansen <ferroclass@michael-jansen.biz> -->
<!-- SPDX-License-Identifier: MPL-2.0 -->

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.12.0] - 2026-05-27

### Added

- **`make test-vendor` target** to verify that vendored dependencies produce a
  working build without network access (`cargo build/test --frozen --config`).
- **Class mappings in PyO3 API.** The `ext_pillar()`, `top()`, and `load()`
  functions now accept `class_mappings` (list of pattern strings) and
  `class_mappings_match_path` (boolean) keyword arguments, matching the
  Python reclass adapter interface.
- **Salt adapter contrib modules.** Pure-Python shim files for Salt's
  `ext_pillar` and `master_tops` interfaces, shipped as reference files in
  `/usr/share/ferroclass/contrib/`. Users copy or symlink them into Salt's
  `extension_modules` directory.

### Changed

- **Vendored sources configuration redesigned.** The committed
  `.cargo/config.toml` no longer contains the `crates-io``vendored-sources`
  replacement. Regular `cargo build` and `cargo test` use crates.io directly
  without requiring a vendor step. Vendored builds use
  `.cargo/config.vendor.toml` (generated by `make vendor`, gitignored) via
  `cargo --config`. The vendor tarball for RPM builds contains a merged
  `.cargo/config.toml` with both build flags and vendored source
  configuration.
- **`make build` no longer depends on `make vendor`.** Building from the
  working tree now uses crates.io dependencies directly, eliminating the
  mandatory vendor step for development builds.
- **`make release` target reordered.** The git tag is now created before
  the tarballs (`tag` before `dist`), fixing the chicken-and-egg bug where
  `dist` checked for a tag that hadn't been created yet.
- **`make rpm-release` target reordered.** The RPM tag is created before
  the tarballs for consistency with the `release` target.
- **`make vendor` now saves `.cargo/config.vendor.toml`.** In addition to
  populating the `vendor/` directory, the target saves the Cargo vendor
  config so `make test-vendor` can use it.

### Fixed

- **`.cargo/config.toml` no longer forces vendored sources.** The previous
  config broke `cargo build`, `cargo test`, and `cargo publish` when the
  `vendor/` directory didn't exist (which is gitignored). Source tarballs
  are now usable standalone without vendor dependencies.
- **`cargo publish` no longer requires `--registry crates-io` workaround.**
  Since the committed config no longer replaces the crates-io source,
  publishing works with the standard `cargo publish` command.

### Removed

- **Vendored source replacement removed from committed `.cargo/config.toml`.**
  The `[source.crates-io]` and `[source.vendored-sources]` sections are no
  longer in the committed file. They are generated at build time when
  vendoring is needed.

## [0.11.1] - 2026-05-26

### Added

- **PyO3 Python bindings** (`python` feature gate). Compile the Rust library
  as a native Python extension module (`cdylib`) using PyO3 0.23. Provides
  `ferroclass` Python package with `ext_pillar()`, `top()`, and `load()`
  functions for Salt integration and direct inventory access.
- **`python3-ferroclass` RPM subpackage** (SUSE only, maturin-built wheel).
  Other distros can opt in with `--with python_subpackage`.
- **`ferroclass-salt-adapter` RPM subpackage** (noarch). Installs Salt adapter
  reference files to `/usr/share/ferroclass/contrib/`.
- **`make wheel`, `make pip-install`, and `make publish-pypi` targets** for
  Python wheel building and PyPI publication via maturin.
- **`pyproject.toml`** for maturin-based Python wheel builds.

### Changed

- **RPM build simplified.** Removed `cargo-rpm-macros` / `rust-packaging`
  BuildRequires; removed `%cargo_prep`. The spec uses plain
  `cargo build --release --frozen` commands instead of RPM cargo macros.
- **Debug packages disabled** (`%define debug_package %{nil}`). Cargo
  release builds strip by default, and the Rust binary and Python `.so`
  would collide on the same debuginfo package name.

### Fixed

- **`python-bytecode-inconsistent-mtime` rpmlint warning** fixed by using
  `pip install --no-compile` so RPM's `brp-python-bytecompile` produces
  consistent `.pyc` files.

## [0.11.0] - 2026-05-21

### Added

- **API documentation.** Added module-level (`//!`) and item-level (`///`) doc
  comments across all public modules: `inventory`, `output`, `configuration`,
  `storage`, `cli`, and their sub-modules. Core types (`Inventory`, `Value`,
  `Key`, `Error` enums, output adapters) are fully documented.
- **Added `make doc` and `make docclean` targets** to the Makefile.
- **Added `documentation = "https://docs.rs/ferroclass"`** to `Cargo.toml`.

### Changed

- **Tightened public API surface.** Internal implementation modules
  (`applications`, `interpolation`, `inv_query`, `merge`, `value_merge`,
  `configuration_file`, `parser`) are now `pub(crate)` instead of `pub`. Selective
  re-exports (`MergeError`, `ValueMergeError`, `merge_values`) are available
  from the `inventory` module.
- **Added crate-root re-exports.** The most commonly used types are now
  accessible directly from `ferroclass::` (e.g., `ferroclass::Inventory`,
  `ferroclass::load`, `ferroclass::Options`, `ferroclass::StorageOptions`).
- **`docclean` Makefile target** now removes the entire `target/doc/` directory.

### Removed

- **Public access to internal modules removed.** Code that referenced
  `ferroclass::inventory::value_merge::merge` should use
  `ferroclass::inventory::merge_values` instead. Code that referenced
  `ferroclass::inventory::merge::Error` should use
  `ferroclass::inventory::MergeError` instead. This is a breaking change.

## [0.10.1] - 2026-05-19

### Changed

- **Improved error messages.** Eliminated pass-through error layers using
  `#[snafu(transparent)]` so `caused by:` chains are shorter, clearer, and free
  of redundant entries like "interpolation error" and "merge error". Error
  messages follow the funnel principle: each line adds exactly one new fact.
- Added `docs/conventions.md` documenting error handling design rules, snafu
  patterns, source chain discipline, and transparent variant usage.
- Updated `AGENTS.md` and `CONTRIBUTING.md` to reference `docs/conventions.md`.

## [0.10.0] - 2026-05-18

### Changed

- **Renamed binaries to avoid conflicts with Python reclass.** The installed
  binaries are now `ferroclass`, `ferroclass-ansible`, and `ferroclass-salt`
  (previously `reclass`, `reclass-ansible`, `reclass-salt`). This allows
  ferroclass to coexist alongside the Python reclass package on the same
  system without file conflicts on `/usr/bin/reclass*`. RPM sub-packages are
  also renamed: `ferroclass-ansible` and `ferroclass-salt`.
- Added `GH_REMOTE` Makefile variable (default: `github`) for configuring
  the git remote name used by the `tag` target.

## [0.9.0] - 2026-05-16

First public release. Feature-complete reimplementation of Python reclass
([salt-formulas/reclass](https://github.com/salt-formulas/reclass)) with 100% CLI
compatibility for the `reclass`, `reclass-ansible`, and `reclass-salt` commands.

### Added

- Full class inheritance with depth-first resolution
- Parameter interpolation (`${...}` references, nested references)
- Inventory queries (`$[...]` syntax: VALUE, TEST, LIST_TEST)
- Two-pass rendering pipeline for cross-node references
- Override prefix (`~key`) for replacing values instead of merging
- Constant prefix (`=key`) for locking values against later changes
- Class name interpolation with `${...}` in the classes list
- Relative class names (`.` and `..` prefix)
- Class mappings (glob/regex patterns matching node names)
- Environment support (default `base`, per-node environment)
- Exports with interpolation and inventory query resolution
- Applications list with `~` negation prefix
- `compose_node_name` for subdirectory-based node names
- `ignore_class_notfound` with regexp filtering
- `ignore_overwritten_missing_reference`
- `inventory_ignore_failed_node` / `inventory_ignore_failed_render`
- `group_errors` for error summarization
- Output formats: YAML and JSON (with pretty-print and sort options)
- Ansible dynamic inventory adapter (`--list` / `--host`)
- Salt external node classifier adapter (`--top` / `--pillar` / `--out`)
- Man pages for all three commands
- RPM packaging (SUSE and RHEL conditional macros)

### Known Incompatibilities

See [README.md](README.md#reclass-compatibility) for the full list.