Skip to content

Add Hiera defaults support for module documentation#27

Merged
bastelfreak merged 10 commits intovoxpupuli:mainfrom
slauger:feature/hiera-defaults-support
Feb 19, 2026
Merged

Add Hiera defaults support for module documentation#27
bastelfreak merged 10 commits intovoxpupuli:mainfrom
slauger:feature/hiera-defaults-support

Conversation

@slauger
Copy link
Copy Markdown
Member

@slauger slauger commented Jan 25, 2026

Summary

This PR adds support for reading parameter defaults from Hiera data files (data/common.yaml) when generating module documentation with openvox-strings.

Previously, openvox-strings could only document parameter defaults that were explicitly defined in Puppet code. With this feature, defaults can now be maintained in Hiera data files while still appearing correctly in the generated REFERENCE.md documentation.

Motivation

Many Puppet modules use Hiera for default values to enable easier customization and separation of data from code. However, this created a documentation gap - parameters without code defaults wouldn't show any default value in the generated documentation, even though they had defaults in data/common.yaml.

Implementation

Core Changes

  1. New OpenvoxStrings::Hiera class (lib/openvox-strings/hiera.rb)

    • Parses hiera.yaml configuration
    • Loads data from data/common.yaml
    • Provides lookup_default(class_name, param_name) method
    • Converts Ruby values to Puppet-compatible string representation
  2. Integration in OpenvoxStrings::Markdown::Base (lib/openvox-strings/markdown/base.rb)

    • Modified defaults method to merge code defaults with Hiera defaults
    • Code defaults take precedence (backwards compatible)
    • Automatically detects module root from manifest file paths
    • Only activates when hiera.yaml exists

Key Features

  • Backwards compatible: Code defaults always take precedence over Hiera defaults
  • Zero configuration: Automatically detects and uses Hiera if hiera.yaml exists
  • All data types supported: String, Integer, Float, Boolean, Array, Hash, nil/undef, nested structures
  • Exact formatting match: Output is byte-for-byte identical to code defaults format

Data Type Conversions

The implementation correctly handles all Puppet data types:

  • Strings: 'value' (quoted)
  • Numbers: 42, 3.14 (unquoted)
  • Booleans: true, false (lowercase, unquoted)
  • nil/undef: undef
  • Empty collections: [], {}
  • Arrays: ['a', 'b', 'c'] (no spaces around brackets to match puppet-strings format)
  • Hashes: { 'key' => 'value' } (spaces inside braces)
  • Nested structures: Recursively converted

Testing

Unit Tests

Comprehensive test suite in spec/unit/openvox-strings/hiera_spec.rb covering:

  • Hiera configuration loading
  • Data file parsing
  • All Puppet data types
  • Nested structures
  • Edge cases (missing files, wrong module names, etc.)
  • Array formatting without spaces to match code defaults

Integration Tests

Three-stage integration test in slauger/puppet-github_actions_runner:

  1. Test 1 - Baseline (PR#4)

    • Uses OLD openvox-strings (6.0.0 from RubyGems)
    • All defaults in code
    • Establishes baseline REFERENCE.md
  2. Test 2 - Backwards Compatibility (PR#5)

    • Uses NEW openvox-strings (this PR)
    • All defaults still in code
    • Proves 100% backwards compatibility
  3. Test 3 - Full Hiera (PR#6)

    • Uses NEW openvox-strings (this PR)
    • ALL defaults moved from code to data/common.yaml
    • Proves Hiera defaults feature works correctly

Result: All three tests produce byte-for-byte identical REFERENCE.md documentation.

Test Data Types Covered

The integration tests validate all common Puppet data types:

  • Integer: 42
  • Float: 3.14
  • Array (empty): []
  • Array[String]: ['alpha', 'beta', 'gamma']
  • Array[Integer]: [1, 2, 3, 5, 8]
  • Hash (nested): { 'config' => { 'timeout' => 30, 'retry' => true }, 'tags' => ['prod', 'eu'] }
  • Plus all standard module parameters (Strings, Booleans, Optional types, etc.)

Breaking Changes

None. This is a fully backwards-compatible addition:

  • Modules without Hiera: No change in behavior
  • Modules with Hiera but code defaults: Code defaults take precedence
  • Only affects modules that use Hiera defaults WITHOUT code defaults

Documentation

The implementation is self-documenting through:

  • YARD documentation in code
  • Comprehensive unit tests showing usage
  • Integration test examples in real-world module

Related Issues

This addresses the common pain point where modules using Hiera v5 data-in-modules pattern couldn't document their defaults properly.

This enhancement allows openvox-strings to automatically extract and display
parameter defaults from Hiera data (common.yaml) in addition to code defaults.

Features:
- New Hiera parser class that reads hiera.yaml and data/common.yaml
- Automatic module root detection from manifest file paths
- Seamless integration with existing markdown generator
- Code defaults take precedence over Hiera defaults
- Support for all Puppet data types (strings, integers, booleans, hashes, arrays, undef)

Implementation:
- lib/openvox-strings/hiera.rb: Core Hiera parsing and value conversion
- lib/openvox-strings/markdown/base.rb: Extended defaults() method to merge Hiera data
- spec/unit/openvox-strings/hiera_spec.rb: Comprehensive unit tests

This allows module maintainers to define defaults in data/common.yaml without
duplicating them in the Puppet class parameters, while still generating complete
documentation.
Empty hashes and arrays should be formatted as {} and [] (without spaces)
to match the formatting of code defaults, ensuring identical REFERENCE.md
generation.
Arrays must not have spaces around brackets to match the format used
by puppet-strings when reading defaults from code:
- Code format: ['a', 'b', 'c']
- Hiera format was: [ 'a', 'b', 'c' ] (WRONG)
- Hiera format now: ['a', 'b', 'c'] (CORRECT)

This ensures byte-for-byte identical REFERENCE.md output whether
defaults come from code or from Hiera data.
During development we discovered that arrays must be formatted without
spaces around brackets to match puppet-strings code defaults format:
- Correct: ['a', 'b', 'c']
- Wrong: [ 'a', 'b', 'c' ]

Added explicit tests to ensure this formatting is preserved:
- String arrays without spaces
- Integer arrays without spaces
- Nested arrays in hashes without spaces
@bastelfreak
Copy link
Copy Markdown
Member

@ekohl I think you will like this

- Combine duplicate branches for Integer/Float/TrueClass/FalseClass
- Use %w[] notation for word arrays in tests
- All tests still pass with the same behavior
Copy link
Copy Markdown
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

puppetlabs#273 was my attempt a few years ago but I never found the time to finish it. It took a bigger approach of also expanding facts so you got a complete table.

slauger and others added 2 commits January 26, 2026 15:53
use Pathname to check file/folder existence - metadata.json, manifests, hiera.yaml

Co-authored-by: Ewoud Kohl van Wijngaarden <ewoud@kohlvanwijngaarden.nl>
- Use dynamic hierarchy parsing instead of hardcoded common.yaml
- Find first hierarchy layer without interpolations (%{...})
- Support various file names (common.yaml, common.yml, defaults.yaml)
- Fix RuboCop indentation violation in find_module_root method
@slauger
Copy link
Copy Markdown
Member Author

slauger commented Jan 26, 2026

Instead of hardcoding common.yaml, the code now parses the hierarchy from hiera.yaml and automatically finds the first layer without interpolations (%{...}). This makes it work with any file naming convention (common.yaml, common.yml, defaults.yaml, etc.) and respects the hierarchy order.

Split load_common_data into two focused methods for better testability:
- find_first_static_layer_path: Finds the first static hierarchy layer
- load_yaml_data: Loads and parses YAML data files

Added comprehensive unit tests for both new methods covering edge cases
like interpolated paths, custom datadirs, and invalid YAML files.
Replace regex pattern with simpler string check in production code
(path.include?('%{') instead of regex) and add RuboCop disable
comments for test fixtures that contain Hiera interpolation syntax.

- Use .include?('%{') for cleaner interpolation detection
- Add Style/FormatStringToken disable in test fixtures
- Fix string literal quote style in tests
@slauger
Copy link
Copy Markdown
Member Author

slauger commented Jan 27, 2026

Feel free to squash the commits when merging.

@slauger
Copy link
Copy Markdown
Member Author

slauger commented Jan 29, 2026

@ekohl any feedback on the latest changes?

Copy link
Copy Markdown
Member

@ekohl ekohl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From reading the code I think this looks correct. There are 2 minor things inline, but that shouldn't hold back the merge.

Anyone else would like to weigh in before we merge this?

- Reuse load_yaml_data in load_hiera_config to reduce code duplication
- Check all entries in paths array for interpolations, not just the first
- Add test for mixed paths array scenario
@slauger
Copy link
Copy Markdown
Member Author

slauger commented Feb 7, 2026

@ekohl addressed your feedback in e215273:

  • Simplified load_hiera_config by reusing load_yaml_data
  • Fixed paths array handling - now checks all entries for interpolations, not just the first
  • Added test case proving the bug with mixed paths arrays

Ready to merge when you are.

@slauger
Copy link
Copy Markdown
Member Author

slauger commented Feb 13, 2026

How should we proceed with this? Since this is my first contribution to this repo, I can't merge it myself.

bastelfreak added a commit to bastelfreak/puppet-systemd that referenced this pull request Feb 13, 2026
@bastelfreak
Copy link
Copy Markdown
Member

Hi @slauger . I gave this a test in voxpupuli/puppet-systemd#607. I regenerated the REFERENCE.md with bundle exec rake strings:generate:reference but that didn't change the REFERENCE.md. is there something else that needs to be configured? Did you test this in a module?

@slauger
Copy link
Copy Markdown
Member Author

slauger commented Feb 13, 2026

@bastelfreak the first match in the hiera.yaml of puppet-systemd w/o interpolation is a common.yaml.

https://github.com/voxpupuli/puppet-systemd/blob/master/hiera.yaml#L15

But there is no common.yaml inside the data directory, so no changes are expected. For PRs with test data checkout the links in the first post.

@bastelfreak bastelfreak added the enhancement New feature or request label Feb 13, 2026
@bastelfreak
Copy link
Copy Markdown
Member

Ah right, it just parses the static paths. I tested it in voxpupuli/puppet-yum#375. thanks for the great work!

@bastelfreak
Copy link
Copy Markdown
Member

I need to think a minute if we want to release this as breaking change or enhancement. breaking changes aren't automatically rolled out to our modules. We have a CI check that validates the REFERENCE.md. This change would break the pipeline for modules with a REFERENCE.md + a static hiera data file.

@bastelfreak bastelfreak added backwards-incompatible and removed enhancement New feature or request labels Feb 19, 2026
@bastelfreak bastelfreak merged commit 1740f2c into voxpupuli:main Feb 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants