Repository: score-spec/score-compose
Pull request #133 (2024-05-07 13:46:07)

fix(provisioner): typo in rabbit provisioner causing bugs when genera…
This fixes a small issue in the rabbitmq provisioner which would always produce random vhost names on each generate call instead of re-using the vhost name stored in the state of the resource.



Patch:
diff --git a/README.md b/README.md
index 2ec98ae..5c82e2c 100644
--- a/README.md
+++ b/README.md
@@ -117,7 +117,7 @@ Examples:
   score-compose generate score.yaml --override-file=./overrides.score.yaml --override-property=metadata.key=value
 
 Flags:
-      --build stringArray               An optional build context to use for the given container --build=container=./dir or --build=container={'"context":"./dir"}
+      --build stringArray               An optional build context to use for the given container --build=container=./dir or --build=container={"context":"./dir"}
       --env-file string                 Location to store a skeleton .env file for compose - this will override existing content
   -h, --help                            help for generate
       --image string                    An optional container image to use for any container with image == '.'
diff --git a/internal/command/default.provisioners.yaml b/internal/command/default.provisioners.yaml
index d0b4daa..cec287f 100644
--- a/internal/command/default.provisioners.yaml
+++ b/internal/command/default.provisioners.yaml
@@ -307,7 +307,7 @@
     publishPort: {{ dig "annotations" "compose.score.dev/publish-port" "0" .Metadata | atoi }}
     publishManagementPort: {{ dig "annotations" "compose.score.dev/publish-management-port" "0" .Metadata | atoi }}
   state: |
-    vhost: {{ dig "database" .Init.randomVHost .State | quote }}
+    vhost: {{ dig "vhost" .Init.randomVHost .State | quote }}
     username: {{ dig "username" .Init.randomUsername .State | quote }}
     password: {{ dig "password" .Init.randomPassword .State | quote }}
   outputs: |
@@ -378,6 +378,7 @@
     {{ if ne .Init.publishManagementPort 0 }}
     - "{{.Uid}}: Browse the rabbitmq UI at \"http://localhost:{{ .Init.publishManagementPort }}\""
     {{ end }}
+
 # The default dns provisioner just outputs localhost as the hostname every time.
 # This is because without actual control of a dns resolver we can't do any accurate routing on any other name. This
 # can be replaced by a new provisioner in the future.
diff --git a/internal/command/generate.go b/internal/command/generate.go
index bd4d955..eec505b 100644
--- a/internal/command/generate.go
+++ b/internal/command/generate.go
@@ -16,13 +16,13 @@ package command
 
 import (
 	"context"
-	"encoding/json"
 	"fmt"
 	"log/slog"
 	"os"
 	"slices"
 	"strings"
 
+	composeloader "github.com/compose-spec/compose-go/v2/loader"
 	"github.com/compose-spec/compose-go/v2/types"
 	"github.com/imdario/mergo"
 	"github.com/score-spec/score-go/framework"
@@ -158,10 +158,12 @@ arguments.
 						return fmt.Errorf("invalid --%s '%s': unknown container '%s'", generateCmdBuildFlag, buildFlag, parts[0])
 					}
 					if strings.HasPrefix(parts[1], "{") {
+						var intermediate interface{}
+						if err := yaml.Unmarshal([]byte(parts[1]), &intermediate); err != nil {
+							return fmt.Errorf("invalid --%s '%s': %w", generateCmdBuildFlag, buildFlag, err)
+						}
 						var out types.BuildConfig
-						dec := json.NewDecoder(strings.NewReader(parts[1]))
-						dec.DisallowUnknownFields()
-						if err := dec.Decode(&out); err != nil {
+						if err := composeloader.Transform(intermediate, &out); err != nil {
 							return fmt.Errorf("invalid --%s '%s': %w", generateCmdBuildFlag, buildFlag, err)
 						}
 						containerBuildContexts[parts[0]] = out
@@ -327,7 +329,7 @@ func init() {
 	generateCommand.Flags().String(generateCmdOverridesFileFlag, "", "An optional file of Score overrides to merge in")
 	generateCommand.Flags().StringArray(generateCmdOverridePropertyFlag, []string{}, "An optional set of path=key overrides to set or remove")
 	generateCommand.Flags().String(generateCmdImageFlag, "", "An optional container image to use for any container with image == '.'")
-	generateCommand.Flags().StringArray(generateCmdBuildFlag, []string{}, "An optional build context to use for the given container --build=container=./dir or --build=container={'\"context\":\"./dir\"}")
+	generateCommand.Flags().StringArray(generateCmdBuildFlag, []string{}, "An optional build context to use for the given container --build=container=./dir or --build=container={\"context\":\"./dir\"}")
 	generateCommand.Flags().String(generateCmdEnvFileFlag, "", "Location to store a skeleton .env file for compose - this will override existing content")
 	rootCmd.AddCommand(generateCommand)
 }


Test patch:
diff --git a/internal/command/generate_test.go b/internal/command/generate_test.go
index 1b3e8d2..4bdb7b3 100644
--- a/internal/command/generate_test.go
+++ b/internal/command/generate_test.go
@@ -53,7 +53,7 @@ Examples:
   score-compose generate score.yaml --override-file=./overrides.score.yaml --override-property=metadata.key=value
 
 Flags:
-      --build stringArray               An optional build context to use for the given container --build=container=./dir or --build=container={'"context":"./dir"}
+      --build stringArray               An optional build context to use for the given container --build=container=./dir or --build=container={"context":"./dir"}
       --env-file string                 Location to store a skeleton .env file for compose - this will override existing content
   -h, --help                            help for generate
       --image string                    An optional container image to use for any container with image == '.'
@@ -270,6 +270,44 @@ services:
 		assert.Equal(t, expectedOutput, string(raw))
 	})
 
+	t.Run("generate with json build context and array args", func(t *testing.T) {
+		stdout, _, err = executeAndResetCommand(context.Background(), rootCmd, []string{
+			"generate", "-o", "compose-output.yaml", "--build", `example={"context":"./dir","args":["DEBUG"]}`, "--", "score.yaml",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, "", stdout)
+		raw, err := os.ReadFile(filepath.Join(td, "compose-output.yaml"))
+		assert.NoError(t, err)
+		expectedOutput := `name: "001"
+services:
+    example-example:
+        build:
+            context: ./dir
+            args:
+                DEBUG: null
+        hostname: example
+`
+		assert.Equal(t, expectedOutput, string(raw))
+	})
+
+	t.Run("generate with yaml build context", func(t *testing.T) {
+		stdout, _, err = executeAndResetCommand(context.Background(), rootCmd, []string{
+			"generate", "-o", "compose-output.yaml", "--build", `example={context: "./dir"}`, "--", "score.yaml",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, "", stdout)
+		raw, err := os.ReadFile(filepath.Join(td, "compose-output.yaml"))
+		assert.NoError(t, err)
+		expectedOutput := `name: "001"
+services:
+    example-example:
+        build:
+            context: ./dir
+        hostname: example
+`
+		assert.Equal(t, expectedOutput, string(raw))
+	})
+
 }
 
 func TestInitAndGenerate_with_files(t *testing.T) {