Skip to content

Add /update-config to bring an existing config.yml up to date without overwriting it #35

Description

@milanmalhotra

Problem

Set Homes Two never rewrites a config.yml that already exists (SetHomesTwo.initConfig, which returns early when the file is present). That is the right default, since it protects an admin's customisations, but it means every setting added in a later release is invisible to an upgrading server. The value silently falls back to its default and the admin cannot change a setting they cannot see.

This is not theoretical, and it is not a one-release problem. On a real test server upgrading through several releases:

  • shipped default-config.yml: 65 top-level keys
  • that server's config.yml: 21 keys, so 44 missing

Missing keys included teleportSafety, checkForUpdates, updateReminderDays, every home management GUI setting, and every message added since the file was written.

The README currently tells owners to copy keys out of default-config.yml by hand, or to rename their file and copy their old values back. Both work, and both are exactly the kind of chore that does not get done.

Proposed

A /update-config command that adds missing settings to the existing config.yml without touching anything already in it.

Requirements:

  • Only add, never replace. An existing key keeps the admin's value, whatever it is.
  • Preserve comments, both the admin's own and the shipped ones. The comments in default-config.yml are the documentation for each setting, so a merge that strips them makes the result worse than the problem.
  • Back up first, to something like config.yml.bak, before writing.
  • Idempotent. Running it twice adds nothing the second time.
  • Report what it did, naming the keys added, and reload the config so a restart is not required.
  • Gate on a new op-default permission, and add it to the sh2.admin bundle.

The catch worth knowing before implementing

A naive key-level merge, for example config.options().copyDefaults(true) plus saveConfig(), would not have solved the case that prompted this issue.

The permissions: block, the one an admin most often needs to discover, is shipped entirely commented out:

# -- PERMISSIONS --
# ... 21 comment lines, 0 real keys ...
# permissions:
#   sh2.import-homes: op
#   sh2.manage-homes: false

There is no permissions key in the file, so no key-copying approach will ever bring it across. The same applies to any future commented-out example block.

So the merge has to work at the level of blocks of text, not keys. Suggested shape:

  1. Parse default-config.yml into blocks, where a block is a run of comment lines plus the top-level key line and any indented children beneath it. Comment-only blocks, like the permissions example, count as blocks in their own right.
  2. Read the admin's config.yml with YamlConfiguration to determine which top-level keys are already present. Use the parsed YAML rather than regex, so an unusually formatted file is still read correctly.
  3. Append each block whose key is absent, grouped under the # -- SECTION -- headers default-config.yml already uses, writing a section header only when at least one block below it is being added.
  4. A comment-only block is added when none of the keys it documents are present.

Appending rather than merging in place means an admin who has reordered or restructured their file is never disturbed.

Alternatives considered

  • Write config.yml.new beside the existing file and let the admin diff and swap. Zero risk of damaging their file, but it puts the work back on them, which is what the issue is trying to remove. Reasonable fallback if the in-place merge proves fragile.
  • Overwrite on upgrade. Not acceptable. It would destroy customisations.

Worth shipping alongside, and much cheaper

Whatever happens to the command, the plugin could log a warning at startup naming the count and the missing keys:

SetHomesTwo: your config.yml is missing 44 settings added in later releases.
Run /update-config, or copy them from default-config.yml.

That turns a silent gap into a visible one and is a few lines. It is arguably worth doing even if the command itself is never built.

Acceptance criteria

  • Running the command on a config missing keys adds exactly those keys, with their shipped comments.
  • Every pre-existing key keeps its original value and its original comments.
  • The commented-out permissions: example is added when the admin has no permissions block.
  • Running the command a second time changes nothing (byte-identical file).
  • A backup is written before any change.
  • The config is reloaded, so the added settings take effect without a restart.
  • Covered by MockBukkit tests: a truncated config, values preserved, idempotency, comment-only block handling.
  • README, default-config.yml and a changeset entry updated.

Notes

Found while running the in-game verification pass for the v1 parity and permission config work. Deliberately kept off that branch, which is finished and reviewed. This is its own feature and needs its own changeset.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions