{"id":50987191,"url":"https://github.com/yaml/go-yaml-dom","last_synced_at":"2026-06-24T23:00:26.211Z","repository":{"id":365785805,"uuid":"1273747534","full_name":"yaml/go-yaml-dom","owner":"yaml","description":null,"archived":false,"fork":false,"pushed_at":"2026-06-18T22:27:11.000Z","size":41,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-21T20:20:18.261Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/yaml.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":"Contributing.md","funding":null,"license":"License","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-18T20:45:37.000Z","updated_at":"2026-06-19T10:05:06.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/yaml/go-yaml-dom","commit_stats":null,"previous_names":["yaml/go-yaml-dom"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/yaml/go-yaml-dom","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaml%2Fgo-yaml-dom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaml%2Fgo-yaml-dom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaml%2Fgo-yaml-dom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaml%2Fgo-yaml-dom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yaml","download_url":"https://codeload.github.com/yaml/go-yaml-dom/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yaml%2Fgo-yaml-dom/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34665261,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-22T02:00:06.391Z","response_time":106,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-19T21:00:54.400Z","updated_at":"2026-06-22T21:00:30.151Z","avatar_url":"https://github.com/yaml.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# go-yaml-dom\n\n`go-yaml-dom` is a dependency-free Go library of structural operations over\ngo-yaml v4 representation nodes (`*yaml.Node`): merge, find, update in place,\nclone, and compare.\n\nIts only dependency is `go.yaml.in/yaml/v4`.\n\n```sh\ngo get github.com/yaml/go-yaml-dom\n```\n\nStatus: prototype.\n\nThis module supports Go 1.18 and is tested with Go 1.18. The repository\nMakefiles install Go 1.18.10 locally through Makes, so a system Go installation\nis not required for development.\n\n## Purpose\n\nUse `go-yaml-dom` when you want direct structural operations on go-yaml's\nrepresentation graph without pulling in an expression engine or non-YAML format\nadapters. It is the live, in-place companion to `go-yaml-yq`.\n\n## Contract\n\n`dom` works on live `*yaml.Node` graphs from `go.yaml.in/yaml/v4`.\n\n- `Merge` mutates the destination node in place.\n- `FindNodes` and `FindNode` return live interior pointers.\n- `Update` mutates those live pointers in place.\n- `Clone` returns a detached deep copy.\n- `Equal` compares node content and ignores style, comments, and source positions.\n\nThis is intentionally different from expression engines that return copies. If you\npass a copied node into `Merge` or `Update`, only that copy changes.\n\n## Loading And Dumping YAML\n\nUse go-yaml v4 directly to load and dump data:\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\tyaml \"go.yaml.in/yaml/v4\"\n)\n\nfunc load(s string) *yaml.Node {\n\tvar n yaml.Node\n\tif err := yaml.Unmarshal([]byte(s), \u0026n); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn \u0026n\n}\n\nfunc dump(n *yaml.Node) {\n\tout, err := yaml.Marshal(n)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Print(string(out))\n}\n```\n\n## API\n\n```go\nfunc Merge(dst, src *yaml.Node, opts ...MergeOption) error\n\nfunc FindNodes(root *yaml.Node, pred func(*yaml.Node) bool) []*yaml.Node\nfunc FindNode(root *yaml.Node, pred func(*yaml.Node) bool) (*yaml.Node, error)\n\nfunc Update(nodes []*yaml.Node, fn func(*yaml.Node) error) error\n\nfunc Clone(node *yaml.Node) *yaml.Node\nfunc Equal(a, b *yaml.Node) bool\n```\n\n## Merge\n\n`Merge` deep-merges `src` into `dst` in place.\n\n```go\nerr := dom.Merge(base, overlay)\n```\n\nFor a non-destructive merge, clone first:\n\n```go\nmerged := dom.Clone(base)\nerr := dom.Merge(merged, overlay)\n```\n\nBy default, source nodes are deep-copied into the destination so later source\nmutations do not bleed into the merged tree.\n\nMerge options:\n\n- `WithSequenceMerge(dom.SequenceReplace)` replaces sequences, the default.\n- `WithAppendSequences()` appends source sequence items.\n- `WithSequenceMerge(dom.SequenceByIndex)` merges sequence items by position.\n- `WithOnlyExistingKeys()` updates only keys already present in the destination.\n- `WithOnlyNewKeys()` adds only keys absent from the destination.\n- `WithClobberTags()` lets source custom tags replace destination tags.\n- `WithNullMerge(dom.NullOverwrite)` lets source null replace destination values, the default.\n- `WithNullMerge(dom.NullIgnore)` ignores source null values.\n- `WithNullMerge(dom.NullDelete)` removes destination mapping keys when source values are null.\n- `WithSharedSource()` grafts source nodes by pointer instead of deep-copying them.\n\n## Find And Update\n\n`FindNodes` walks a node tree in pre-order and returns live pointers to matching\nnodes. It takes a Go predicate, not a path DSL. Mapping keys are visited. Document\nwrappers are traversed but not passed to the predicate. Alias targets are not\nfollowed.\n\n`FindNode` is strict: it returns an error unless exactly one node matches.\n\n`Update` applies a function to live nodes in place and stops at the first error.\n\n## Clone\n\n`Clone` returns a fully detached deep copy. Anchor and alias structure is\npreserved: aliases in the clone point at cloned anchors, not the original graph.\n\n## Equal\n\n`Equal` compares deep node content: kind, tag, value, children, and alias\nstructure. It ignores style, comments, and source positions.\n\n## Example: Basic Merge\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/yaml/go-yaml-dom\"\n\tyaml \"go.yaml.in/yaml/v4\"\n)\n\nconst baseYAML = `\nservice:\n  image: app:v1\n  replicas: 1\n  env:\n    LOG_LEVEL: info\n`\n\nconst overlayYAML = `\nservice:\n  replicas: 3\n  env:\n    FEATURE_FLAG: enabled\n`\n\nfunc main() {\n\tbase := load(baseYAML)\n\toverlay := load(overlayYAML)\n\n\tif err := dom.Merge(base, overlay); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdump(base)\n}\n\nfunc load(s string) *yaml.Node {\n\tvar n yaml.Node\n\tif err := yaml.Unmarshal([]byte(s), \u0026n); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\treturn \u0026n\n}\n\nfunc dump(n *yaml.Node) {\n\tout, err := yaml.Marshal(n)\n\tif err != nil {\n\t\tlog.Fatal(err)\n\t}\n\tfmt.Print(string(out))\n}\n```\n\nOutput:\n\n```yaml\nservice:\n    image: app:v1\n    replicas: 3\n    env:\n        LOG_LEVEL: info\n        FEATURE_FLAG: enabled\n```\n\n## Example: Append Sequences\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/yaml/go-yaml-dom\"\n\tyaml \"go.yaml.in/yaml/v4\"\n)\n\nconst baseYAML = `\npipeline:\n  steps:\n    - checkout\n    - test\n`\n\nconst overlayYAML = `\npipeline:\n  steps:\n    - package\n    - deploy\n`\n\nfunc main() {\n\tbase := load(baseYAML)\n\toverlay := load(overlayYAML)\n\n\tif err := dom.Merge(base, overlay, dom.WithAppendSequences()); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdump(base)\n}\n```\n\nOutput:\n\n```yaml\npipeline:\n    steps:\n        - checkout\n        - test\n        - package\n        - deploy\n```\n\n## Example: Find And Update\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/yaml/go-yaml-dom\"\n\tyaml \"go.yaml.in/yaml/v4\"\n)\n\nconst inputYAML = `\nservices:\n  api:\n    image: app:v1\n  worker:\n    image: worker:v1\n`\n\nfunc main() {\n\tdoc := load(inputYAML)\n\n\timages := dom.FindNodes(doc, func(n *yaml.Node) bool {\n\t\treturn n.Kind == yaml.ScalarNode \u0026\u0026 strings.HasSuffix(n.Value, \":v1\")\n\t})\n\n\tif err := dom.Update(images, func(n *yaml.Node) error {\n\t\tn.Value = strings.TrimSuffix(n.Value, \":v1\") + \":v2\"\n\t\treturn nil\n\t}); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tdump(doc)\n}\n```\n\nOutput:\n\n```yaml\nservices:\n    api:\n        image: app:v2\n    worker:\n        image: worker:v2\n```\n\n## Example: Non-Destructive Merge\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/yaml/go-yaml-dom\"\n\tyaml \"go.yaml.in/yaml/v4\"\n)\n\nconst baseYAML = `\napp:\n  image: app:v1\n`\n\nconst overlayYAML = `\napp:\n  replicas: 2\n`\n\nfunc main() {\n\tbase := load(baseYAML)\n\toverlay := load(overlayYAML)\n\n\tmerged := dom.Clone(base)\n\tif err := dom.Merge(merged, overlay); err != nil {\n\t\tlog.Fatal(err)\n\t}\n\n\tfmt.Println(\"--- original\")\n\tdump(base)\n\tfmt.Println(\"--- merged\")\n\tdump(merged)\n}\n```\n\nOutput:\n\n```yaml\n--- original\napp:\n    image: app:v1\n--- merged\napp:\n    image: app:v1\n    replicas: 2\n```\n\n## Running The Examples\n\nThe runnable versions of these examples live under `examples/`.\n\n```sh\nmake -C examples build\nmake -C examples run\nmake -C examples clean\n```\n\nRun one example:\n\n```sh\nmake -C examples/basic-merge run\n```\n\nEach example directory has its own `ReadMe.md` and supports:\n\n```sh\nmake build\nmake run\nmake clean\n```\n\n## Development\n\nThe root Makefile bootstraps Makes into `.cache/makes` and installs the pinned\nGo toolchain into `.cache/local`.\n\nCommon targets:\n\n```sh\nmake test        # go test ./...\nmake vet         # go vet ./...\nmake verify      # fmt, tidy, vet, test\nmake examples    # build all example programs\nmake test-all    # tests plus example smoke runs\nmake clean       # remove example binaries\nmake deps        # print the module graph\n```\n\n## CI\n\nGitHub Actions runs tests, hygiene checks, example smoke tests, Staticcheck,\nand CodeQL. Hygiene includes formatting, `go.mod`/`go.sum`, file lint, spelling,\nand dependency graph checks. The dependency graph check verifies that the module\ngraph contains only this module and `go.yaml.in/yaml/v4 v4.0.0-rc.5`.\n\nDependabot is configured for Go modules and GitHub Actions.\n\n## Works With go-yaml-yq\n\n`go-yaml-dom` and `go-yaml-yq` compose only through `*yaml.Node`; neither imports\nthe other. Use yq expressions when you want path/query/expression power, then use\n`dom.Merge` or `dom.Update` for live in-place structural operations.\n\nKeep both modules on the same `go.yaml.in/yaml/v4` version while yaml/v4 is\npre-1.0, otherwise `*yaml.Node` can resolve to distinct types.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaml%2Fgo-yaml-dom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyaml%2Fgo-yaml-dom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyaml%2Fgo-yaml-dom/lists"}