Skip to content

Commit a7657dc

Browse files
committed
fix: Use yaml.v3 Node API to preserve comments and structure in regup
- Switched from goccy/go-yaml to gopkg.in/yaml.v3 Node API - Now properly preserves comments and YAML structure - Successfully tested on github entry: - Comments preserved - Structure maintained - Metadata updated correctly The regup tool now works as expected without destroying the file format.
1 parent 2c7f0cf commit a7657dc

File tree

1 file changed

+90
-96
lines changed

1 file changed

+90
-96
lines changed

cmd/regup/main.go

Lines changed: 90 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package main
33

44
import (
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

Comments
 (0)