22package main
33
44import (
5+ "bytes"
56 "context"
67 "encoding/json"
78 "errors"
@@ -13,8 +14,6 @@ import (
1314 "strings"
1415 "time"
1516
16- "github.com/goccy/go-yaml/ast"
17- "github.com/goccy/go-yaml/parser"
1817 "github.com/spf13/cobra"
1918 "github.com/stacklok/toolhive-registry/pkg/types"
2019 "github.com/stacklok/toolhive/pkg/container/verifier"
@@ -200,7 +199,7 @@ func updateServerInfo(server serverWithName) error {
200199 logger .Infof ("Updating %s: stars %d -> %d, pulls %d -> %d" ,
201200 server .name , currentStars , newStars , currentPulls , newPulls )
202201
203- // Use goccy/go- yaml to preserve comments and structure
202+ // Use yaml.v3 Node API to preserve comments and structure
204203 return updateYAMLPreservingStructure (server .path , newStars , newPulls )
205204}
206205
@@ -212,119 +211,114 @@ func updateYAMLPreservingStructure(path string, stars, pulls int) error {
212211 return fmt .Errorf ("failed to read file: %w" , err )
213212 }
214213
215- // Parse the YAML preserving comments
216- file , err := parser . ParseBytes ( data , parser . ParseComments )
217- if err != nil {
214+ // Parse with yaml.v3 to preserve structure
215+ var doc yaml. Node
216+ if err := yaml . Unmarshal ( data , & doc ); err != nil {
218217 return fmt .Errorf ("failed to parse YAML: %w" , err )
219218 }
220219
221- // Find and update the metadata section
222- for _ , doc := range file .Docs {
223- if err := updateMetadataInAST (doc .Body , stars , pulls ); err != nil {
224- // If metadata doesn't exist, we need to add it
225- if err .Error () == "metadata not found" {
226- addMetadataToAST (doc .Body , stars , pulls )
227- } else {
228- return err
229- }
230- }
220+ // Update the metadata fields
221+ if err := updateMetadataInNode (& doc , stars , pulls ); err != nil {
222+ return fmt .Errorf ("failed to update metadata: %w" , err )
223+ }
224+
225+ // Marshal back preserving structure
226+ var buf bytes.Buffer
227+ encoder := yaml .NewEncoder (& buf )
228+ encoder .SetIndent (4 )
229+ if err := encoder .Encode (& doc ); err != nil {
230+ return fmt .Errorf ("failed to encode YAML: %w" , err )
231231 }
232232
233233 // Write back to file
234- return os .WriteFile (path , [] byte ( file . String () ), 0644 )
234+ return os .WriteFile (path , buf . Bytes ( ), 0644 )
235235}
236236
237- // updateMetadataInAST updates metadata fields in the AST
238- func updateMetadataInAST (node ast .Node , stars , pulls int ) error {
239- mapping , ok := node .( * ast. MappingNode )
240- if ! ok {
241- return fmt . Errorf ( "expected mapping node" )
237+ // updateMetadataInNode updates metadata fields in the YAML node tree
238+ func updateMetadataInNode (node * yaml .Node , stars , pulls int ) error {
239+ // Navigate to the document content
240+ if node . Kind == yaml . DocumentNode && len ( node . Content ) > 0 {
241+ return updateMetadataInNode ( node . Content [ 0 ], stars , pulls )
242242 }
243243
244- for _ , value := range mapping .Values {
245- if value .Key .String () == "metadata" {
246- metaMapping , ok := value .Value .(* ast.MappingNode )
247- if ! ok {
248- continue
249- }
244+ if node .Kind != yaml .MappingNode {
245+ return fmt .Errorf ("expected mapping node, got %v" , node .Kind )
246+ }
250247
251- // Update existing fields
252- hasStars , hasPulls , hasLastUpdated := false , false , false
253- for _ , metaValue := range metaMapping .Values {
254- switch metaValue .Key .String () {
255- case "stars" :
256- metaValue .Value = & ast.IntegerNode {
257- Value : fmt .Sprintf ("%d" , stars ),
258- }
259- hasStars = true
260- case "pulls" :
261- metaValue .Value = & ast.IntegerNode {
262- Value : fmt .Sprintf ("%d" , pulls ),
263- }
264- hasPulls = true
265- case "lastupdated" :
266- metaValue .Value = & ast.StringNode {
267- Value : time .Now ().UTC ().Format (time .RFC3339 ),
268- }
269- hasLastUpdated = true
270- }
271- }
248+ // Find or create metadata section
249+ metadataIndex := - 1
250+ for i := 0 ; i < len (node .Content ); i += 2 {
251+ if node .Content [i ].Value == "metadata" {
252+ metadataIndex = i
253+ break
254+ }
255+ }
272256
273- // Add missing fields
274- if ! hasStars {
275- metaMapping .Values = append (metaMapping .Values , & ast.MappingValueNode {
276- Key : & ast.StringNode {Value : "stars" },
277- Value : & ast.IntegerNode {Value : fmt .Sprintf ("%d" , stars )},
278- })
279- }
280- if ! hasPulls {
281- metaMapping .Values = append (metaMapping .Values , & ast.MappingValueNode {
282- Key : & ast.StringNode {Value : "pulls" },
283- Value : & ast.IntegerNode {Value : fmt .Sprintf ("%d" , pulls )},
284- })
285- }
286- if ! hasLastUpdated {
287- metaMapping .Values = append (metaMapping .Values , & ast.MappingValueNode {
288- Key : & ast.StringNode {Value : "lastupdated" },
289- Value : & ast.StringNode {Value : time .Now ().UTC ().Format (time .RFC3339 )},
290- })
291- }
257+ now := time .Now ().UTC ().Format (time .RFC3339 )
292258
293- return nil
259+ if metadataIndex >= 0 {
260+ // Update existing metadata
261+ metadataNode := node .Content [metadataIndex + 1 ]
262+ if metadataNode .Kind != yaml .MappingNode {
263+ return fmt .Errorf ("metadata is not a mapping" )
294264 }
295- }
296265
297- return fmt .Errorf ("metadata not found" )
298- }
266+ // Update or add fields
267+ updated := map [string ]bool {
268+ "stars" : false ,
269+ "pulls" : false ,
270+ "lastupdated" : false ,
271+ }
299272
300- // addMetadataToAST adds a metadata section to the AST
301- func addMetadataToAST (node ast.Node , stars , pulls int ) {
302- mapping , ok := node .(* ast.MappingNode )
303- if ! ok {
304- return
305- }
273+ for i := 0 ; i < len (metadataNode .Content ); i += 2 {
274+ key := metadataNode .Content [i ].Value
275+ switch key {
276+ case "stars" :
277+ metadataNode .Content [i + 1 ].Value = fmt .Sprintf ("%d" , stars )
278+ updated ["stars" ] = true
279+ case "pulls" :
280+ metadataNode .Content [i + 1 ].Value = fmt .Sprintf ("%d" , pulls )
281+ updated ["pulls" ] = true
282+ case "lastupdated" :
283+ metadataNode .Content [i + 1 ].Value = now
284+ updated ["lastupdated" ] = true
285+ }
286+ }
306287
307- metaMapping := & ast.MappingNode {
308- Values : []* ast.MappingValueNode {
309- {
310- Key : & ast.StringNode {Value : "stars" },
311- Value : & ast.IntegerNode {Value : fmt .Sprintf ("%d" , stars )},
312- },
313- {
314- Key : & ast.StringNode {Value : "pulls" },
315- Value : & ast.IntegerNode {Value : fmt .Sprintf ("%d" , pulls )},
316- },
317- {
318- Key : & ast.StringNode {Value : "lastupdated" },
319- Value : & ast.StringNode {Value : time .Now ().UTC ().Format (time .RFC3339 )},
288+ // Add missing fields
289+ if ! updated ["stars" ] {
290+ metadataNode .Content = append (metadataNode .Content ,
291+ & yaml.Node {Kind : yaml .ScalarNode , Value : "stars" },
292+ & yaml.Node {Kind : yaml .ScalarNode , Value : fmt .Sprintf ("%d" , stars )})
293+ }
294+ if ! updated ["pulls" ] {
295+ metadataNode .Content = append (metadataNode .Content ,
296+ & yaml.Node {Kind : yaml .ScalarNode , Value : "pulls" },
297+ & yaml.Node {Kind : yaml .ScalarNode , Value : fmt .Sprintf ("%d" , pulls )})
298+ }
299+ if ! updated ["lastupdated" ] {
300+ metadataNode .Content = append (metadataNode .Content ,
301+ & yaml.Node {Kind : yaml .ScalarNode , Value : "lastupdated" },
302+ & yaml.Node {Kind : yaml .ScalarNode , Value : now })
303+ }
304+ } else {
305+ // Add new metadata section
306+ metadataKey := & yaml.Node {Kind : yaml .ScalarNode , Value : "metadata" }
307+ metadataValue := & yaml.Node {
308+ Kind : yaml .MappingNode ,
309+ Content : []* yaml.Node {
310+ {Kind : yaml .ScalarNode , Value : "stars" },
311+ {Kind : yaml .ScalarNode , Value : fmt .Sprintf ("%d" , stars )},
312+ {Kind : yaml .ScalarNode , Value : "pulls" },
313+ {Kind : yaml .ScalarNode , Value : fmt .Sprintf ("%d" , pulls )},
314+ {Kind : yaml .ScalarNode , Value : "lastupdated" },
315+ {Kind : yaml .ScalarNode , Value : now },
320316 },
321- },
317+ }
318+ node .Content = append (node .Content , metadataKey , metadataValue )
322319 }
323320
324- mapping .Values = append (mapping .Values , & ast.MappingValueNode {
325- Key : & ast.StringNode {Value : "metadata" },
326- Value : metaMapping ,
327- })
321+ return nil
328322}
329323
330324// verifyServerProvenance verifies the provenance information for a server
0 commit comments