diff --git a/.changeset/sunny-donkeys-hang.md b/.changeset/sunny-donkeys-hang.md new file mode 100644 index 00000000..fdc7ab41 --- /dev/null +++ b/.changeset/sunny-donkeys-hang.md @@ -0,0 +1,8 @@ +--- +"chainlink-deployments-framework": patch +--- + +fix(input): remove handling of object format + +object format for input yaml is no longer supported, +only array format is supported. diff --git a/engine/cld/commands/pipeline/input_generate_test.go b/engine/cld/commands/pipeline/input_generate_test.go index 6fee1a52..0cffd7b1 100644 --- a/engine/cld/commands/pipeline/input_generate_test.go +++ b/engine/cld/commands/pipeline/input_generate_test.go @@ -95,9 +95,9 @@ func TestInputGenerateCmd_WithOutputFile(t *testing.T) { inputsContent := `environment: testnet domain: test changesets: - 0001_cs: - payload: - x: 1` + - 0001_cs: + payload: + x: 1` require.NoError(t, os.WriteFile(filepath.Join(inputsDir, "in.yaml"), []byte(inputsContent), 0o644)) //nolint:gosec originalWd, _ := os.Getwd() diff --git a/engine/cld/legacy/cli/commands/durable-pipelines.go b/engine/cld/legacy/cli/commands/durable-pipelines.go index e4560313..54d52120 100644 --- a/engine/cld/legacy/cli/commands/durable-pipelines.go +++ b/engine/cld/legacy/cli/commands/durable-pipelines.go @@ -322,37 +322,11 @@ func (c Commands) newDurablePipelineInputGenerate( } } - // Resolve every changeset in the file - var orderedChangesets []map[string]any // For both formats to preserve order and duplicates + // Resolve every changeset in the file. + var orderedChangesets []map[string]any - // Handle both object and array formats for changesets - //nolint:exhaustive // Only handling MappingNode and SequenceNode cases for changesets + //nolint:exhaustive // SequenceNode is the only valid format for changesets switch dpFile.Changesets.Kind { - case yaml.MappingNode: - // Object format: changesets: { key1: {payload: ...}, key2: {payload: ...} } - orderedChangesets = make([]map[string]any, 0, len(dpFile.Changesets.Content)/2) - // yaml.Node for a mapping has Content with alternating key-value pairs - for i := 0; i < len(dpFile.Changesets.Content); i += 2 { - keyNode := dpFile.Changesets.Content[i] - valueNode := dpFile.Changesets.Content[i+1] - - csName := keyNode.Value - resolver, ok := resolverByKey[csName] - if !ok { - resolver = nil // No resolver registered for this changeset - } - - resolvedCfg, err2 := resolveChangesetConfig(valueNode, csName, resolver) - if err2 != nil { - return err2 - } - - // For object format, store each changeset as a separate item (same as array format) - changesetItem := map[string]any{ - csName: map[string]any{"payload": resolvedCfg}, - } - orderedChangesets = append(orderedChangesets, changesetItem) - } case yaml.SequenceNode: // Array format: changesets: [ { key1: {payload: ...} }, { key2: {payload: ...} } ] orderedChangesets = make([]map[string]any, 0, len(dpFile.Changesets.Content)) @@ -383,73 +357,38 @@ func (c Commands) newDurablePipelineInputGenerate( orderedChangesets = append(orderedChangesets, changesetItem) } default: - return fmt.Errorf("changesets must be either an object (mapping) or an array (sequence), got %v", dpFile.Changesets.Kind) + return fmt.Errorf("changesets must be an array (sequence), got %v", dpFile.Changesets.Kind) } - // Build ordered output structure using yaml.Node to preserve order and original format - var changesetsNode *yaml.Node + changesetsNode := &yaml.Node{ + Kind: yaml.SequenceNode, + } - if dpFile.Changesets.Kind == yaml.MappingNode { - // Object format: preserve as object - changesetsNode = &yaml.Node{ + for _, changesetItem := range orderedChangesets { + // Create a mapping node for each changeset item. + itemNode := &yaml.Node{ Kind: yaml.MappingNode, } - for _, changesetItem := range orderedChangesets { - // Each changesetItem has one key-value pair - for csName, csConfig := range changesetItem { - // Add key node - keyNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: csName, - } - changesetsNode.Content = append(changesetsNode.Content, keyNode) - - // Add value node - valueNode := &yaml.Node{} - err = valueNode.Encode(csConfig) - if err != nil { - return fmt.Errorf("encode changeset value for %s: %w", csName, err) - } - changesetsNode.Content = append(changesetsNode.Content, valueNode) - - break // Only one key-value pair per item - } - } - } else { - // Array format: preserve as array - changesetsNode = &yaml.Node{ - Kind: yaml.SequenceNode, - } - - for _, changesetItem := range orderedChangesets { - // Create a mapping node for each changeset item - itemNode := &yaml.Node{ - Kind: yaml.MappingNode, + // Each changesetItem has one key-value pair. + for csName, csConfig := range changesetItem { + keyNode := &yaml.Node{ + Kind: yaml.ScalarNode, + Value: csName, } + itemNode.Content = append(itemNode.Content, keyNode) - // Each changesetItem has one key-value pair - for csName, csConfig := range changesetItem { - // Add key node - keyNode := &yaml.Node{ - Kind: yaml.ScalarNode, - Value: csName, - } - itemNode.Content = append(itemNode.Content, keyNode) - - // Add value node - valueNode := &yaml.Node{} - err = valueNode.Encode(csConfig) - if err != nil { - return fmt.Errorf("encode changeset value for %s: %w", csName, err) - } - itemNode.Content = append(itemNode.Content, valueNode) - - break // Only one key-value pair per item + valueNode := &yaml.Node{} + err = valueNode.Encode(csConfig) + if err != nil { + return fmt.Errorf("encode changeset value for %s: %w", csName, err) } + itemNode.Content = append(itemNode.Content, valueNode) - changesetsNode.Content = append(changesetsNode.Content, itemNode) + break // Only one key-value pair per item. } + + changesetsNode.Content = append(changesetsNode.Content, itemNode) } // Create the final output structure diff --git a/engine/cld/legacy/cli/commands/durable-pipelines_test.go b/engine/cld/legacy/cli/commands/durable-pipelines_test.go index fd1bc1d8..d0db3108 100644 --- a/engine/cld/legacy/cli/commands/durable-pipelines_test.go +++ b/engine/cld/legacy/cli/commands/durable-pipelines_test.go @@ -212,18 +212,6 @@ func TestNewDurablePipelineInputGenerateCmd(t *testing.T) { inputsFileName string mockInputContent string }{ - { - name: "object format", - formatDescription: "legacy object format", - inputsFileName: "test-inputs-object.yaml", - mockInputContent: `environment: testnet -domain: test -changesets: - 0001_test_changeset: - payload: - chain: optimism_sepolia - value: 100`, - }, { name: "array format", formatDescription: "new array format", diff --git a/engine/cld/pipeline/input/generate.go b/engine/cld/pipeline/input/generate.go index b0b8f8a3..eb8c5ec2 100644 --- a/engine/cld/pipeline/input/generate.go +++ b/engine/cld/pipeline/input/generate.go @@ -70,27 +70,8 @@ func Generate(opts GenerateOptions) (string, error) { var orderedChangesets []map[string]any - //nolint:exhaustive // Only MappingNode and SequenceNode are valid for changesets + //nolint:exhaustive // SequenceNode is the only valid format for changesets switch dpFile.Changesets.Kind { - case yaml.MappingNode: - orderedChangesets = make([]map[string]any, 0, len(dpFile.Changesets.Content)/2) - for i := 0; i < len(dpFile.Changesets.Content); i += 2 { - keyNode := dpFile.Changesets.Content[i] - valueNode := dpFile.Changesets.Content[i+1] - - csName := keyNode.Value - resolver := resolverByKey[csName] - - resolvedCfg, resolveErr := ResolveChangesetConfig(valueNode, csName, resolver) - if resolveErr != nil { - return "", resolveErr - } - - changesetItem := map[string]any{ - csName: map[string]any{"payload": resolvedCfg}, - } - orderedChangesets = append(orderedChangesets, changesetItem) - } case yaml.SequenceNode: orderedChangesets = make([]map[string]any, 0, len(dpFile.Changesets.Content)) for _, itemNode := range dpFile.Changesets.Content { @@ -115,45 +96,25 @@ func Generate(opts GenerateOptions) (string, error) { orderedChangesets = append(orderedChangesets, changesetItem) } default: - return "", fmt.Errorf("changesets must be either an object (mapping) or an array (sequence), got %v", dpFile.Changesets.Kind) + return "", fmt.Errorf("changesets must be an array (sequence), got %v", dpFile.Changesets.Kind) } - var changesetsNode *yaml.Node - - if dpFile.Changesets.Kind == yaml.MappingNode { - changesetsNode = &yaml.Node{Kind: yaml.MappingNode} - for _, changesetItem := range orderedChangesets { - for csName, csConfig := range changesetItem { - keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: csName} - changesetsNode.Content = append(changesetsNode.Content, keyNode) + changesetsNode := &yaml.Node{Kind: yaml.SequenceNode} + for _, changesetItem := range orderedChangesets { + itemNode := &yaml.Node{Kind: yaml.MappingNode} + for csName, csConfig := range changesetItem { + keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: csName} + itemNode.Content = append(itemNode.Content, keyNode) - valueNode, nodeErr := anyToYAMLNode(csConfig) - if nodeErr != nil { - return "", fmt.Errorf("encode changeset value for %s: %w", csName, nodeErr) - } - changesetsNode.Content = append(changesetsNode.Content, valueNode) - - break + valueNode, nodeErr := anyToYAMLNode(csConfig) + if nodeErr != nil { + return "", fmt.Errorf("encode changeset value for %s: %w", csName, nodeErr) } + itemNode.Content = append(itemNode.Content, valueNode) + + break } - } else { - changesetsNode = &yaml.Node{Kind: yaml.SequenceNode} - for _, changesetItem := range orderedChangesets { - itemNode := &yaml.Node{Kind: yaml.MappingNode} - for csName, csConfig := range changesetItem { - keyNode := &yaml.Node{Kind: yaml.ScalarNode, Value: csName} - itemNode.Content = append(itemNode.Content, keyNode) - - valueNode, nodeErr := anyToYAMLNode(csConfig) - if nodeErr != nil { - return "", fmt.Errorf("encode changeset value for %s: %w", csName, nodeErr) - } - itemNode.Content = append(itemNode.Content, valueNode) - - break - } - changesetsNode.Content = append(changesetsNode.Content, itemNode) - } + changesetsNode.Content = append(changesetsNode.Content, itemNode) } finalOutputNode := &yaml.Node{ diff --git a/engine/cld/pipeline/input/generate_test.go b/engine/cld/pipeline/input/generate_test.go index f29874fc..d478ccba 100644 --- a/engine/cld/pipeline/input/generate_test.go +++ b/engine/cld/pipeline/input/generate_test.go @@ -24,51 +24,6 @@ func (g *generateStubChangeset) VerifyPreconditions(_ fdeployment.Environment, _ var _ fdeployment.ChangeSetV2[any] = (*generateStubChangeset)(nil) -func generateTestResolver(m map[string]any) (any, error) { - return map[string]any{"resolved": true, "v": m["v"]}, nil -} - -//nolint:paralleltest -func TestGenerate_ObjectFormat(t *testing.T) { - dir := t.TempDir() - require.NoError(t, os.MkdirAll(filepath.Join(dir, "domains"), 0o755)) - inputsDir := filepath.Join(dir, "domains", "mydomain", "testnet", "durable_pipelines", "inputs") - require.NoError(t, os.MkdirAll(inputsDir, 0o755)) - - inputsContent := `environment: testnet -domain: mydomain -changesets: - 0001_cs1: - payload: - v: 1 -` - require.NoError(t, os.WriteFile(filepath.Join(inputsDir, "in.yaml"), []byte(inputsContent), 0o644)) //nolint:gosec - - originalWd, _ := os.Getwd() - require.NoError(t, os.Chdir(dir)) - t.Cleanup(func() { _ = os.Chdir(originalWd) }) - - rm := fresolvers.NewConfigResolverManager() - rm.Register(generateTestResolver, fresolvers.ResolverInfo{Description: "X"}) - - reg := cs.NewChangesetsRegistry() - reg.Add("0001_cs1", cs.Configure(&generateStubChangeset{}).WithConfigResolver(generateTestResolver)) - - dom := domain.NewDomain(dir, "mydomain") - opts := GenerateOptions{ - InputsFileName: "in.yaml", - Domain: dom, - EnvKey: "testnet", - Registry: reg, - ResolverManager: rm, - FormatAsJSON: false, - } - - got, err := Generate(opts) - require.NoError(t, err) - require.Equal(t, "environment: testnet\ndomain: mydomain\nchangesets:\n 0001_cs1:\n payload:\n resolved: true\n v: 1\n", got) -} - //nolint:paralleltest func TestGenerate_ArrayFormat(t *testing.T) { dir := t.TempDir() @@ -108,7 +63,19 @@ changesets: got, err := Generate(opts) require.NoError(t, err) - require.JSONEq(t, "{\n \"changesets\": [\n {\n \"0001_cs1\": {\n \"payload\": {\n \"x\": 1\n }\n }\n }\n ],\n \"domain\": \"mydomain\",\n \"environment\": \"testnet\"\n}", got) + require.JSONEq(t, `{ + "changesets": [ + { + "0001_cs1": { + "payload": { + "x": 1 + } + } + } + ], + "domain": "mydomain", + "environment": "testnet" +}`, got) } //nolint:paralleltest @@ -120,7 +87,7 @@ func TestGenerate_InvalidChangesetsFormat(t *testing.T) { inputsContent := `environment: testnet domain: mydomain -changesets: "not-object-or-array" +changesets: "invalid-changesets-format" ` require.NoError(t, os.WriteFile(filepath.Join(inputsDir, "in.yaml"), []byte(inputsContent), 0o644)) //nolint:gosec @@ -140,7 +107,7 @@ changesets: "not-object-or-array" _, err := Generate(opts) require.Error(t, err) - require.Equal(t, "changesets must be either an object (mapping) or an array (sequence), got 8", err.Error()) + require.ErrorContains(t, err, "changesets must be an array (sequence)") } //nolint:paralleltest @@ -153,9 +120,9 @@ func TestGenerate_ResolverNotRegistered(t *testing.T) { inputsContent := `environment: testnet domain: mydomain changesets: - 0001_cs1: - payload: - x: 1 + - 0001_cs1: + payload: + x: 1 ` require.NoError(t, os.WriteFile(filepath.Join(inputsDir, "in.yaml"), []byte(inputsContent), 0o644)) //nolint:gosec