Skip to content

yaml: pure-Go Psych loader + generic object dump → puppet apply exits cleanly - #40

Merged
tannevaled merged 3 commits into
mainfrom
puppet-yaml-clean
Jun 29, 2026
Merged

tannevaled merged 3 commits into
mainfrom
puppet-yaml-clean

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

What

Makes puppet apply exit with zero Error: lines under rbgo (the pure-Go CGO=0 Ruby). The previous milestone converged notify/file resources and emitted their Notices, but every run still printed three trailing errors:

Error: Checksumfile .../state.yaml is corrupt (#<Puppet::Util::Yaml::YamlLoadError>); replacing
Error: Could not write report ... undefined method 'to_yaml' for Puppet::Transaction::Report
Error: Could not save yaml : #<TypeError>   (x2)

All three are gone.

Root causes & fixes

  1. No YAML loader. rbgo had a Psych-compatible emitter but YAML.load / safe_load / load_file raised NotImplementedError, so Puppet::Util::Yaml.load_file(state.yaml) failed and Puppet logged "state.yaml is corrupt; replacing" every run. Added a pure-Go Psych-subset parser (yaml_load.go): block + flow mappings/sequences, plain / single- / double-quoted / literal+folded block scalars, the Psych scalar keywords (null/bool/int/float/hex/bignum/ISO-8601 Time), symbols, and !ruby/object: / !ruby/symbol tags with &anchor / *alias. YAML.dump(x) → YAML.load round-trips the state/summary structures.

  2. No generic object serialization. Report#to_yaml was undefined and Psych.dump of an arbitrary object raised TypeError. Added Object#to_yaml and extended the emitter to serialise arbitrary objects as !ruby/object:Class with their instance variables (recursively), plus !ruby/range, !ruby/class / !ruby/module, Regexp, and Set (MRI's !ruby/object:Set). Shared / cyclic graph nodes are emitted once behind a YAML anchor and aliased thereafter; complex and nil mapping keys use the explicit ?/: form. The full Puppet::Transaction::Report graph (~10k lines) now writes as syntactically-valid YAML (verified with MRI Psych.parse_file).

  3. Could not save yaml : TypeError (x2). Two causes: the report graph now serialises, and FileSystem#replace_file no longer trips an unimplemented FileUtils.chown when rewriting an existing state.yaml. Implemented FileUtils.chown / chown_R / chmod_R (reusing the existing fileChown seam — a no-op on Windows, as in MRI).

Verification

puppet apply -e 'notify {...}' run twice (state.yaml round-trip only shows on the 2nd run; the report write shows every run) — zero Error: lines both times. The MRI 4.0.5 oracle was used throughout for YAML.load(YAML.dump(x)) round-trips and obj.to_yaml structure. The two Warning: Could not retrieve fact servername/serverip lines come from the intentionally-minimal facter probe shim, not rbgo.

Tests / coverage

Cross-package coverage is 100.0% (the exact gate: grep -v 100.0% | grep -v '^total' is empty), including all parse-error / edge branches. New Ruby-driven suites for load + object dump (matched to MRI structure) plus an internal-package suite for the scalar/flow/tag/anchor helpers, class resolution and the reference-graph walk. go vet clean, gofmt clean, GOOS=windows build + vet clean, frozen-prelude drift test green.

🤖 Generated with Claude Code

tannevaled and others added 3 commits June 29, 2026 07:44
Make `puppet apply` exit with zero Error lines under rbgo. Three trailing
errors are eliminated:

- state.yaml "is corrupt": implement YAML.load / safe_load / load_file as a
  pure-Go Psych-subset parser (block + flow mappings/sequences, plain /
  quoted / literal-block scalars, symbols, ISO-8601 Time, bignums,
  !ruby/object: with &anchor/*alias) that round-trips what the emitter writes.

- report to_yaml undefined: install Object#to_yaml and extend the emitter to
  serialise arbitrary objects as !ruby/object:Class with their ivars
  (recursively), plus !ruby/range, !ruby/class / !ruby/module, Regexp, and
  Set (as MRI's !ruby/object:Set). Shared/cyclic graph nodes are written once
  behind a YAML anchor. Complex and nil mapping keys use the explicit ?/: form.

- "Could not save yaml : TypeError" (x2): the report graph now serialises, and
  FileSystem#replace_file no longer trips FileUtils.chown — implement
  FileUtils.chown / chown_R / chmod_R (reusing the fileChown seam; a no-op on
  Windows as in MRI).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- loader: parse flow collections before block-mapping detection; parse a
  document-root / inline tagged object body at the correct indent; load
  &anchor/*alias graphs; inline "tag {}" empty objects.
- dump: emit object/range/set body keys as plain identifiers (not quoted
  strings); anchor shared plain collections; fix explicit-key ":" indent and
  the standalone collection anchor spacing.
- add YAML load + object-dump test suites (round-trips vs MRI structure).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fixes

- add an internal-package test suite covering the scalar/flow/tag/anchor
  helpers, class resolution, countRefs graph walk and the encoder bodies.
- remove unreachable defaults (dead Array/Hash cases in countRefs, the nil
  anchor-map guard, the openTag/tagBody trailing returns) so every branch is
  reachable.
- fix a bare trailing "-" to load as nil (not an empty Hash) and the inline-
  first complex-key indentation (double "?" pad).

Cross-package coverage is 100.0% with no function below 100%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 5f7ca1e into main Jun 29, 2026
9 checks passed
@tannevaled
tannevaled deleted the puppet-yaml-clean branch June 29, 2026 07:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant