Repository: score-spec/score-compose
Pull request #162 (2024-07-08 16:09:30)

feat: rename default provisioners file and change header comment
Users may be confused with the numbering based system which forces them to pick a number between 0 and 99 to override default provisioners. It's much easier if the file is prefixed with zz- so that a numeric prefix is not needed and the lexicographic sorting order makes more sense!

Additionally, I've removed the license header from the yaml file, since this file isn't really an artefact that we are distributing and is not required for proper functioning of the binary, rather it is a template file that we encourage folks to modify, extend, or override. This has the side effect of resolving issues like #158 or others which may have concerns about the license header being checked into their repo or distributed in their CI.



Patch:
diff --git a/internal/command/default.provisioners.yaml b/internal/command/default.provisioners.yaml
index 2a9e7dc..51631b4 100644
--- a/internal/command/default.provisioners.yaml
+++ b/internal/command/default.provisioners.yaml
@@ -1,16 +1,6 @@
-# Copyright 2024 Humanitec
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
+# This file is generated by score-compose when score-compose init is called. This presents the default set of
+# template provisioners provided for a basic set of resource types. This file can be overridden by adding new
+# *.provisioners.yaml files in this directory or by editing this file in place and checking it into the source code.
 
 # The default volume provisioner provided by score-compose allows basic volume resources to be created in the resources
 # system. The volume resource just creates an ephemeral Docker volume with a random string as the name, and source
diff --git a/internal/command/init.go b/internal/command/init.go
index 50911ca..9d93405 100644
--- a/internal/command/init.go
+++ b/internal/command/init.go
@@ -152,8 +152,10 @@ acts as a namespace when multiple score files and containers are used.
 			}
 
 			// create and write the default provisioners file if it doesn't already exist
-			dst := "99-default" + loader.DefaultSuffix
-			if f, err := os.OpenFile(filepath.Join(sd.Path, dst), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644); err != nil {
+			dst := "zz-default" + loader.DefaultSuffix
+			if f, err := os.Stat(filepath.Join(sd.Path, "99-default"+loader.DefaultSuffix)); err == nil {
+				slog.Info(fmt.Sprintf("Default provisioners yaml file '%s' already exists, not overwriting it", f.Name()))
+			} else if f, err := os.OpenFile(filepath.Join(sd.Path, dst), os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644); err != nil {
 				if !errors.Is(err, os.ErrExist) {
 					return fmt.Errorf("failed to open default provisioners for writing: %w", err)
 				}
diff --git a/internal/command/resources.go b/internal/command/resources.go
index e0a0c98..48f0a9a 100644
--- a/internal/command/resources.go
+++ b/internal/command/resources.go
@@ -18,6 +18,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"log/slog"
+	"strings"
 	"text/template"
 
 	"github.com/Masterminds/sprig/v3"
@@ -95,6 +96,10 @@ be returned as json.
 				case "yaml":
 					return yaml.NewEncoder(cmd.OutOrStdout()).Encode(outputs)
 				default:
+					// ensure there is a new line at the end if one is not already present
+					if !strings.HasSuffix(formatValue, "\n") {
+						formatValue += "\n"
+					}
 					prepared, err := template.New("").Funcs(sprig.FuncMap()).Parse(formatValue)
 					if err != nil {
 						return fmt.Errorf("failed to parse format template: %w", err)


Test patch:
diff --git a/internal/command/init_test.go b/internal/command/init_test.go
index e47a1a1..6dc8b1c 100644
--- a/internal/command/init_test.go
+++ b/internal/command/init_test.go
@@ -190,7 +190,7 @@ func TestInitNominal_run_twice(t *testing.T) {
 	assert.NotEqual(t, "", strings.TrimSpace(stderr))
 
 	// check default provisioners exists and overwrite it with an empty array
-	dpf, err := os.Stat(filepath.Join(td, ".score-compose", "99-default.provisioners.yaml"))
+	dpf, err := os.Stat(filepath.Join(td, ".score-compose", "zz-default.provisioners.yaml"))
 	assert.NoError(t, err)
 	assert.NoError(t, os.WriteFile(filepath.Join(td, ".score-compose", dpf.Name()), []byte("[]"), 0644))
 
diff --git a/internal/command/resources_test.go b/internal/command/resources_test.go
index 5a51cd8..e0a1a81 100644
--- a/internal/command/resources_test.go
+++ b/internal/command/resources_test.go
@@ -19,6 +19,7 @@ import (
 	"encoding/json"
 	"os"
 	"path/filepath"
+	"strings"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -111,6 +112,7 @@ volume.default#example.vol
 		assert.NoError(t, err)
 		var out map[string]interface{}
 		assert.NoError(t, json.Unmarshal([]byte(stdout), &out))
+		assert.True(t, strings.HasSuffix(stdout, "\n"))
 	})
 
 	t.Run("format yaml", func(t *testing.T) {
@@ -118,11 +120,18 @@ volume.default#example.vol
 		assert.NoError(t, err)
 		var out map[string]interface{}
 		assert.NoError(t, yaml.Unmarshal([]byte(stdout), &out))
+		assert.True(t, strings.HasSuffix(stdout, "\n"))
 	})
 
 	t.Run("format template", func(t *testing.T) {
 		stdout, _, err := executeAndResetCommand(context.Background(), rootCmd, []string{"resources", "get-outputs", "volume.default#example.vol", "--format", `{{ . | len }}`})
 		assert.NoError(t, err)
-		assert.Equal(t, "2", stdout)
+		assert.Equal(t, "2\n", stdout)
+	})
+
+	t.Run("format template with newline", func(t *testing.T) {
+		stdout, _, err := executeAndResetCommand(context.Background(), rootCmd, []string{"resources", "get-outputs", "volume.default#example.vol", "--format", "{{ . | len }}\n"})
+		assert.NoError(t, err)
+		assert.Equal(t, "2\n", stdout)
 	})
 }