Skip to content
On this page

Upgrading from v2 to v3: settings-file keys and directives ​

Sections 2, 16, 32, and 53 of the v2 to v3 guide, whose index page carries the summary table and the order of operations.

2. undeclared becomes _undeclared ​

Every list section wrapper, and every nested one (environments[].variables, environments[].secrets, environments[].deployment_branch_policies, environments[].deployment_protection_rules), spells the knob _undeclared.

yaml
labels:
  _undeclared: keep
  entries:
    - name: bug
      color: "d73a4a"

The old spelling fails validation before any section runs, so a check run finds every stale file at once. A v2 labels wrapper in .github/settings.yml produces this error:

text
.github/settings.yml has malformed section entries: labels: Unrecognized key: "undeclared"; the wrapper's policy key "undeclared" was renamed to "_undeclared" in v3 (a directive, like _layering) - write _undeclared: keep or _undeclared: delete. Fix these values in the settings file (only the named keys are validated; extra fields pass through, except in closed sections and strict nested objects like actions.cache, which reject unrecognized keys)

The underscore marks the action's directives: _undeclared (live axis), _layering (merge time), and _remove (merge time, on one keyed entry), and nothing else (section 16). The undeclared policy page owns the knob.

16. Underscore keys are directives, never notes ​

v2 dropped any unknown top-level key starting with _ as a private note, while rejecting the same key inside a section's {entries} wrapper.

v3 has one rule at the top level and on the wrappers: the underscore belongs to the three directives, and any other underscore key there fails validation before any section runs. Each directive has its place: _layering at the top level or on a list section's wrapper, _undeclared at the top level or on a knobbed wrapper (section 32), and _remove: true on a keyed list entry (section 31).

A directive out of its place is refused like any unknown key: {name: bug, _remove: true} drops an entry, while a top-level _remove: true or labels: {_remove: true, entries: []} fails validation. A removal also needs a fold to act in: in a single document (a one-file apply or check) it fails validation naming its site, since there is no lower layer to remove from.

yaml
# owner: platform-team, see runbook RB-112
labels:
  - name: bug
    color: "d73a4a"

A v2 file with _owner: platform-team at its top level now fails with this line in the collected list (abbreviated):

text
settings.yml has malformed section entries: unknown underscore key: _owner. The underscore marks this action's directives, "_layering" (...) and "_undeclared" (...), and nothing else; there are no private-note keys. Remove the key, or keep the note as a YAML comment (...)

A sections allowlist does not soften it (an unknown plain section outside the allowlist still only warns). The reason is the loud-failure promise: a misspelled _layerin: replace dropped as a note would merge a layer its author meant to replace.

Move each note into a YAML comment; the layering guide states the rule beside the directives.

32. File-wide _undeclared and the undeclared input ​

The policy no longer has to be repeated on every wrapper:

text
pre-release   labels:
                _undeclared: delete
                entries: [...]
              milestones:
                _undeclared: delete
                entries: [...]
              webhooks:
                _undeclared: delete
                entries: [...]

v3            _undeclared: delete          # every knobbed section of this file, unless its wrapper says otherwise
              labels: [...]
              milestones: [...]
              webhooks:
                _undeclared: keep          # the wrapper wins for this one section
                entries: [...]

The run input undeclared (keep or delete, unset by default) sets the same default for every file an apply, check, or render reads; mode: snapshot rejects it. The precedence, highest first: the list's wrapper, then the file's top-level _undeclared, then the undeclared input, then the list's own default the undeclared policy page lists.

A wrong value is refused before any section runs, one line in the collected list (abbreviated):

text
settings.yml has malformed section entries: _undeclared must be one of "keep", "delete"; got a string that is none of them. Write _undeclared: keep or _undeclared: delete at the top of the file, or remove the key so each list's own policy applies (...)

A file-wide delete reaches the nested environments[].deployment_protection_rules list too: an undeclared deployment gate is disabled, where the nested default is keep. Set the nested wrapper to keep on the environment that must keep its gates.

One v2 file breaks: a top-level _undeclared that v2 dropped as a private note (section 16) is now the file-wide policy, so a delete note deletes the undeclared entries of every knobbed section on the first apply. Run mode: check first; the deletions appear there as drift. A file that set every wrapper by hand behaves as before, and a wrapper still beats every default.

The render consumes the top-level key and writes the resolved policy onto every knobbed wrapper, an environment's nested lists included, so a rendered document always carries those nested lists in wrapper form.

53. YAML merge keys resolve ​

text
v2   labels:
       - &base
         name: bug
         color: ff0000
         description: Something broken
       - <<: *base
         name: defect
     -> the second label as parsed: {"<<": {"name": "bug", ...}, "name": "defect"}; the "<<" field rode into the create payload

v3   -> the second label as parsed: {"name": "defect", "color": "ff0000", "description": "Something broken"}

Every reader (single file, layers, central file, defaults file, the CLI) parses through one function, so << resolves the way the Probot Settings app's parser did. Nothing to fix; a file that leaned on the literal << key reaching GitHub had a 422 waiting.

Built from main at ab5102dSource: docs/upgrading/v2-to-v3/settings-file.md