Home

Awesome

Yaml config updater

License CI Appveyor build status codecov

Support: Discussions | Gitter chat

About

Merges yaml configuration files, preserving comments and whitespaces. Assumed to be used for microservice configuration updates.

By default, targets the most common use-case: add all new properties, without removing or changing existing values. Lists not merged because list is a value and all current values must remain.

Introduction article

Comments preserved using custom (simple) yaml parser. Snakeyaml parser used for source files validation, comments parser self-validation (compares parse trees) and result validation.

Due to complete validation, merged file correctness is guaranteed.

Supports:

IMPORTANT: this is not a general-purpose yaml merge tool because yaml features like multiple documents in one file and object references are not supported (only common subset of features, used in configs)

Example

Original config:

# top comment

# something
prop:
  one: 1

  two: 2

lists:

  # sub comment
  list:
    - one
    - two

  obj:
    - one: 1
      two: 2

large: multi-line
  value

# trailing comment

Update file:

# changed comment

# something else
prop:
  one: 3
  two: 3
  three: 3                              # new property

lists:

  # changed sub comment
  list:                                 # padding changed
      - two                             # order changed (ignored)
      - one
      - three                           # new value ignored

  obj:
    - two: 2
      one: 1
      three: 3                        # new value

large: multi-line
  value

# changed trailing comment

Merge result:

# changed comment

# something else
prop:
  one: 1

  two: 2
  three: 3                              # new property

lists:

  # changed sub comment
  list:
      - one
      - two

  obj:
    - two: 2
      one: 1
      three: 3                        # new value

large: multi-line
  value

# changed trailing comment

Merge report (shown by cli modules):

Configuration: /var/somewhere/config.yml (185 bytes, 23 lines)
Updated from source of 497 bytes, 25 lines
Resulted in 351 bytes, 25 lines

	Added from new file:
		prop/three                               7  | three: 3                              # new property
		lists/obj[0]/three                       20 | three: 3                        # new value

Setup

Maven Central

Could be used as:

Read exact module's readme for setup instructions.

Merge rules

<table> <tr> <th>Case</th> <th>Current config</th> <th>Update file</th> <th>Merge result</th> </tr> <tr> <td> New properties added </td> <td> <pre lang="yaml"> one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> one: 3 two: 3 three: 3 </pre> </td> <td> <pre lang="yaml"> one: 1 two: 2 three: 3 </pre> </td> </tr> <tr> <td> Order changed </td> <td> <pre lang="yaml"> one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> three: 3 two: 3 one: 3 </pre> </td> <td> <pre lang="yaml"> three: 3 two: 2 one: 1 </pre> </td> </tr> <tr> <td> Padding changed </td> <td> <pre lang="yaml"> one: two: 2 </pre> </td> <td> <pre lang="yaml"> one: two: 3 </pre> </td> <td> <pre lang="yaml"> one: two: 2 </pre> </td> </tr> <tr> <td> Comment updated </td> <td> <pre lang="yaml"> one: # Old comment two: 2 </pre> </td> <td> <pre lang="yaml"> one: # New comment two: 3 </pre> </td> <td> <pre lang="yaml"> one: # New comment two: 2 </pre> </td> </tr> <tr> <td> Style changed </td> <td> <pre lang="yaml"> "one": 1 </pre> </td> <td> <pre lang="yaml"> one: 1 </pre> </td> <td> <pre lang="yaml"> one: 1 </pre> </td> </tr> <tr> <td> List not merged, but padding updated </td> <td> <pre lang="yaml"> list: - one - two </pre> </td> <td> <pre lang="yaml"> list: - one - three </pre> </td> <td> <pre lang="yaml"> list: - one - two </pre> </td> </tr> <tr> <td> Object list item updated </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 three: 3 </pre> </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 three: 3 </pre> </td> </tr> <tr> <td> List declaration style could change </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> list: - one: 1 two: 2 </pre> </td> </tr> </table>

Lists matching logic

Object list items updated because in some cases lists could be quite complex and contain updatable configuration blocks.

Items match is searched by "the most similarity": selects item containing more properties with the same value.

<table> <tr> <th>Case</th> <th>List item</th> <th>Candidates</th> <th>Match</th> </tr> <tr> <td> Match by value </td> <td> <pre lang="yaml"> one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> - one: 1 two: 3 - one: 1 two: 2 </pre> </td> <td> Item 2 </td> </tr> <tr> <td> Match by "weight" </td> <td> <pre lang="yaml"> one: 1 two: 2 </pre> </td> <td> <pre lang="yaml"> - one: 1 three: 3 - one: 1 two: 2 three: 3 </pre> </td> <td> Item 2 (technically both matches, but first item with one and second item with 2 props) </td> </tr> <tr> <td> Multiple matches </td> <td> <pre lang="yaml"> one: 1 </pre> </td> <td> <pre lang="yaml"> - one: 1 two: 3 - one: 1 two: 2 </pre> </td> <td> No </td> </tr> <tr> <td> Scalar list ignored </td> <td> <pre lang="yaml"> one: 1 sub: - a - b </pre> </td> <td> <pre lang="yaml"> - one: 1 sub: - c - d - one: 1 sub: - e - f </pre> </td> <td> No (both matches, scalar list values ignored) </td> </tr> <tr> <td> Sub list (one sub list element matched) </td> <td> <pre lang="yaml"> one: 1 sub: - a: 1 </pre> </td> <td> <pre lang="yaml"> - one: 1 sub: - a: 2 - a: 1 - one: 1 sub: - b: 1 - b: 2 </pre> </td> <td> Item 1 (sub list has (at least) one match) </td> </tr> </table>

Variables

Update config could contain variables with syntax: #{name}. Such syntax used instead of more common ${..} because:

obj:
  some: #{name}

IMPORTANT: only known placeholders would be replaced (unknown variables will remain unprocessed)

Feature supposed to be used to "adopt" config for current environment before processing. Cli modules would automatically use environment variables.

General workflow

Two last steps performed only if file content changes, otherwise current config remain untouched (to avoid producing redundant backups).

Comments parser specifics

Parser assume everything above property as property comment (even empty lines):

# Large comment

# With empty lines
prop: 1

Exactly because of this comment is always replaced from new file.

For example, assume reordering case:

Original config:

# Large header

# property comment
prop1: 1
prop2: 2

New config:

# Large header

prop2: 2
# property comment
prop1: 1

Without using comments from new file, entire header would be moved below the first property, but with comments update header would remain untouched.

But still, some edge cases possible (with removed properties).

Values

Parser stores property values as everything after colon (including possible in-line comments). The same for multi-line values.

This way, current value could be restored exactly the same as it was in the original file (to avoid re-formatting).

And because of this in-line comments are not recognized and so could "survive" update.


java lib generator