From c861c09cce0e618f4fd60626f700a612c9b1d122 Mon Sep 17 00:00:00 2001
From: p4u1 <p4u1_f4u1@riseup.net>
Date: Mon, 8 Jul 2024 11:26:01 +0200
Subject: [PATCH] refactor(recipe): use method or variable for .env.sample

---
 cli/app/new.go                     |  6 ++----
 cli/recipe/new.go                  |  4 +++-
 cli/recipe/sync.go                 |  4 +++-
 cli/recipe/upgrade.go              |  2 +-
 cli/recipe/version.go              |  8 ++++----
 pkg/app/app.go                     | 20 ++++++--------------
 pkg/envfile/envfile_test.go        | 17 ++++++++---------
 pkg/lint/recipe.go                 | 15 +++++++--------
 pkg/{compose => recipe}/compose.go | 23 +++++++++++------------
 pkg/recipe/files.go                |  5 +----
 pkg/recipe/recipe.go               | 28 +---------------------------
 11 files changed, 47 insertions(+), 85 deletions(-)
 rename pkg/{compose => recipe}/compose.go (86%)

diff --git a/cli/app/new.go b/cli/app/new.go
index d0de12b3ca..823609d961 100644
--- a/cli/app/new.go
+++ b/cli/app/new.go
@@ -2,7 +2,6 @@ package app
 
 import (
 	"fmt"
-	"path"
 
 	"coopcloud.tech/abra/cli/internal"
 	appPkg "coopcloud.tech/abra/pkg/app"
@@ -121,7 +120,7 @@ var appNewCommand = cli.Command{
 		log.Debugf("%s sanitised as %s for new app", internal.Domain, sanitisedAppName)
 
 		if err := appPkg.TemplateAppEnvSample(
-			recipe.Name,
+			r,
 			internal.Domain,
 			internal.NewAppServer,
 			internal.Domain,
@@ -142,8 +141,7 @@ var appNewCommand = cli.Command{
 				log.Fatal(err)
 			}
 
-			envSamplePath := path.Join(config.RECIPES_DIR, recipe.Name, ".env.sample")
-			secretsConfig, err := secret.ReadSecretsConfig(envSamplePath, composeFiles, appPkg.StackName(internal.Domain))
+			secretsConfig, err := secret.ReadSecretsConfig(r.SampleEnvPath, composeFiles, appPkg.StackName(internal.Domain))
 			if err != nil {
 				return err
 			}
diff --git a/cli/recipe/new.go b/cli/recipe/new.go
index 9d6346f27e..57de11fa1a 100644
--- a/cli/recipe/new.go
+++ b/cli/recipe/new.go
@@ -12,6 +12,7 @@ import (
 	"coopcloud.tech/abra/pkg/config"
 	"coopcloud.tech/abra/pkg/git"
 	"coopcloud.tech/abra/pkg/log"
+	"coopcloud.tech/abra/pkg/recipe"
 	"github.com/urfave/cli"
 )
 
@@ -55,6 +56,7 @@ recipe and domain in the sample environment config).
 `,
 	Action: func(c *cli.Context) error {
 		recipeName := c.Args().First()
+		r := recipe.Get2(recipeName)
 
 		if recipeName == "" {
 			internal.ShowSubcommandHelpAndError(c, errors.New("no recipe name provided"))
@@ -80,7 +82,7 @@ recipe and domain in the sample environment config).
 
 		toParse := []string{
 			path.Join(config.RECIPES_DIR, recipeName, "README.md"),
-			path.Join(config.RECIPES_DIR, recipeName, ".env.sample"),
+			r.SampleEnvPath,
 		}
 		for _, path := range toParse {
 			tpl, err := template.ParseFiles(path)
diff --git a/cli/recipe/sync.go b/cli/recipe/sync.go
index c49b8726f3..0e0ef8749e 100644
--- a/cli/recipe/sync.go
+++ b/cli/recipe/sync.go
@@ -10,6 +10,7 @@ import (
 	"coopcloud.tech/abra/pkg/config"
 	gitPkg "coopcloud.tech/abra/pkg/git"
 	"coopcloud.tech/abra/pkg/log"
+	recipePkg "coopcloud.tech/abra/pkg/recipe"
 	"coopcloud.tech/tagcmp"
 	"github.com/AlecAivazis/survey/v2"
 	"github.com/go-git/go-git/v5"
@@ -44,6 +45,7 @@ local file system.
 	BashComplete: autocomplete.RecipeNameComplete,
 	Action: func(c *cli.Context) error {
 		recipe := internal.ValidateRecipe(c)
+		r := recipePkg.Get2(recipe.Name)
 
 		mainApp, err := internal.GetMainAppImage(recipe)
 		if err != nil {
@@ -192,7 +194,7 @@ likely to change.
 		mainService := "app"
 		label := fmt.Sprintf("coop-cloud.${STACK_NAME}.version=%s", nextTag)
 		if !internal.Dry {
-			if err := recipe.UpdateLabel("compose.y*ml", mainService, label); err != nil {
+			if err := r.UpdateLabel("compose.y*ml", mainService, label); err != nil {
 				log.Fatal(err)
 			}
 		} else {
diff --git a/cli/recipe/upgrade.go b/cli/recipe/upgrade.go
index bd8f339aa2..05d44f2cbf 100644
--- a/cli/recipe/upgrade.go
+++ b/cli/recipe/upgrade.go
@@ -294,7 +294,7 @@ You may invoke this command in "wizard" mode and be prompted for input:
 				}
 			}
 			if upgradeTag != "skip" {
-				ok, err := recipe.UpdateTag(image, upgradeTag)
+				ok, err := r.UpdateTag(image, upgradeTag)
 				if err != nil {
 					log.Fatal(err)
 				}
diff --git a/cli/recipe/version.go b/cli/recipe/version.go
index 86ecc9179c..edad6c2664 100644
--- a/cli/recipe/version.go
+++ b/cli/recipe/version.go
@@ -7,9 +7,9 @@ import (
 	"coopcloud.tech/abra/cli/internal"
 	"coopcloud.tech/abra/pkg/autocomplete"
 	"coopcloud.tech/abra/pkg/formatter"
-	"coopcloud.tech/abra/pkg/log"
 	recipePkg "coopcloud.tech/abra/pkg/recipe"
 	"github.com/olekukonko/tablewriter"
+	"github.com/sirupsen/logrus"
 	"github.com/urfave/cli"
 )
 
@@ -42,16 +42,16 @@ var recipeVersionCommand = cli.Command{
 
 		catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline)
 		if err != nil {
-			log.Fatal(err)
+			logrus.Fatal(err)
 		}
 
 		recipeMeta, ok := catl[recipe.Name]
 		if !ok {
-			log.Fatalf("%s is not published on the catalogue?", recipe.Name)
+			logrus.Fatalf("%s is not published on the catalogue?", recipe.Name)
 		}
 
 		if len(recipeMeta.Versions) == 0 {
-			log.Fatalf("%s has no catalogue published versions?", recipe.Name)
+			logrus.Fatalf("%s has no catalogue published versions?", recipe.Name)
 		}
 
 		tableCols := []string{"version", "service", "image", "tag"}
diff --git a/pkg/app/app.go b/pkg/app/app.go
index 3c2ee619f2..dbff9be9c0 100644
--- a/pkg/app/app.go
+++ b/pkg/app/app.go
@@ -358,9 +358,8 @@ func GetAppNames() ([]string, error) {
 
 // TemplateAppEnvSample copies the example env file for the app into the users
 // env files.
-func TemplateAppEnvSample(recipeName, appName, server, domain string) error {
-	envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample")
-	envSample, err := os.ReadFile(envSamplePath)
+func TemplateAppEnvSample(r recipe.Recipe2, appName, server, domain string) error {
+	envSample, err := os.ReadFile(r.SampleEnvPath)
 	if err != nil {
 		return err
 	}
@@ -380,14 +379,14 @@ func TemplateAppEnvSample(recipeName, appName, server, domain string) error {
 		return err
 	}
 
-	newContents := strings.Replace(string(read), recipeName+".example.com", domain, -1)
+	newContents := strings.Replace(string(read), r.Name+".example.com", domain, -1)
 
 	err = os.WriteFile(appEnvPath, []byte(newContents), 0)
 	if err != nil {
 		return err
 	}
 
-	log.Debugf("copied & templated %s to %s", envSamplePath, appEnvPath)
+	log.Debugf("copied & templated %s to %s", r.SampleEnvPath, appEnvPath)
 
 	return nil
 }
@@ -511,15 +510,8 @@ func ExposeAllEnv(stackName string, compose *composetypes.Config, appEnv envfile
 func CheckEnv(app App) ([]envfile.EnvVar, error) {
 	var envVars []envfile.EnvVar
 
-	envSamplePath := path.Join(config.RECIPES_DIR, app.Recipe, ".env.sample")
-	if _, err := os.Stat(envSamplePath); err != nil {
-		if os.IsNotExist(err) {
-			return envVars, fmt.Errorf("%s does not exist?", envSamplePath)
-		}
-		return envVars, err
-	}
-
-	envSample, err := envfile.ReadEnv(envSamplePath)
+	r := recipe.Get2(app.Recipe)
+	envSample, err := r.SampleEnv()
 	if err != nil {
 		return envVars, err
 	}
diff --git a/pkg/envfile/envfile_test.go b/pkg/envfile/envfile_test.go
index 2ecd12a3fd..6dc47136c2 100644
--- a/pkg/envfile/envfile_test.go
+++ b/pkg/envfile/envfile_test.go
@@ -2,7 +2,6 @@ package envfile_test
 
 import (
 	"fmt"
-	"path"
 	"reflect"
 	"slices"
 	"strings"
@@ -117,8 +116,8 @@ func TestCheckEnv(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	envSample, err := envfile.ReadEnv(envSamplePath)
+	r2 := recipe.Get2(r.Name)
+	envSample, err := r2.SampleEnv()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -151,8 +150,8 @@ func TestCheckEnvError(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	envSample, err := envfile.ReadEnv(envSamplePath)
+	r2 := recipe.Get2(r.Name)
+	envSample, err := r2.SampleEnv()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -187,8 +186,8 @@ func TestEnvVarCommentsRemoved(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	envSample, err := envfile.ReadEnv(envSamplePath)
+	r2 := recipe.Get2(r.Name)
+	envSample, err := r2.SampleEnv()
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -219,8 +218,8 @@ func TestEnvVarModifiersIncluded(t *testing.T) {
 		t.Fatal(err)
 	}
 
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	envSample, modifiers, err := envfile.ReadEnvWithModifiers(envSamplePath)
+	r2 := recipe.Get2(r.Name)
+	envSample, modifiers, err := envfile.ReadEnvWithModifiers(r2.SampleEnvPath)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/pkg/lint/recipe.go b/pkg/lint/recipe.go
index 52f24522f3..fce04f20b0 100644
--- a/pkg/lint/recipe.go
+++ b/pkg/lint/recipe.go
@@ -7,7 +7,6 @@ import (
 	"path"
 
 	"coopcloud.tech/abra/pkg/config"
-	"coopcloud.tech/abra/pkg/envfile"
 	"coopcloud.tech/abra/pkg/log"
 	"coopcloud.tech/abra/pkg/recipe"
 	recipePkg "coopcloud.tech/abra/pkg/recipe"
@@ -210,9 +209,9 @@ func LintComposeVersion(recipe recipe.Recipe) (bool, error) {
 	return true, nil
 }
 
-func LintEnvConfigPresent(recipe recipe.Recipe) (bool, error) {
-	envSample := fmt.Sprintf("%s/%s/.env.sample", config.RECIPES_DIR, recipe.Name)
-	if _, err := os.Stat(envSample); !os.IsNotExist(err) {
+func LintEnvConfigPresent(r recipe.Recipe) (bool, error) {
+	r2 := recipe.Get2(r.Name)
+	if _, err := os.Stat(r2.SampleEnvPath); !os.IsNotExist(err) {
 		return true, nil
 	}
 
@@ -233,11 +232,11 @@ func LintAppService(recipe recipe.Recipe) (bool, error) {
 // confirms that there is no "DOMAIN=..." in the .env.sample configuration of
 // the recipe. This typically means that no domain is required to deploy and
 // therefore no matching traefik deploy label will be present.
-func LintTraefikEnabledSkipCondition(recipe recipe.Recipe) (bool, error) {
-	envSamplePath := path.Join(config.RECIPES_DIR, recipe.Name, ".env.sample")
-	sampleEnv, err := envfile.ReadEnv(envSamplePath)
+func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) {
+	r2 := recipe.Get2(r.Name)
+	sampleEnv, err := r2.SampleEnv()
 	if err != nil {
-		return false, fmt.Errorf("Unable to discover .env.sample for %s", recipe.Name)
+		return false, fmt.Errorf("Unable to discover .env.sample for %s", r2.Name)
 	}
 
 	if _, ok := sampleEnv["DOMAIN"]; !ok {
diff --git a/pkg/compose/compose.go b/pkg/recipe/compose.go
similarity index 86%
rename from pkg/compose/compose.go
rename to pkg/recipe/compose.go
index db7a77461a..7e6384ba83 100644
--- a/pkg/compose/compose.go
+++ b/pkg/recipe/compose.go
@@ -1,14 +1,11 @@
-package compose
+package recipe
 
 import (
 	"fmt"
 	"io/ioutil"
-	"path"
 	"path/filepath"
 	"strings"
 
-	"coopcloud.tech/abra/pkg/config"
-	"coopcloud.tech/abra/pkg/envfile"
 	"coopcloud.tech/abra/pkg/formatter"
 	"coopcloud.tech/abra/pkg/log"
 	"coopcloud.tech/abra/pkg/upstream/stack"
@@ -18,8 +15,11 @@ import (
 )
 
 // UpdateTag updates an image tag in-place on file system local compose files.
-func UpdateTag(pattern, image, tag, recipeName string) (bool, error) {
-	composeFiles, err := filepath.Glob(pattern)
+func (r Recipe2) UpdateTag(image, tag string) (bool, error) {
+	fullPattern := fmt.Sprintf("%s/compose**yml", r.Dir)
+	image = formatter.StripTagMeta(image)
+
+	composeFiles, err := filepath.Glob(fullPattern)
 	if err != nil {
 		return false, err
 	}
@@ -29,8 +29,7 @@ func UpdateTag(pattern, image, tag, recipeName string) (bool, error) {
 	for _, composeFile := range composeFiles {
 		opts := stack.Deploy{Composefiles: []string{composeFile}}
 
-		envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample")
-		sampleEnv, err := envfile.ReadEnv(envSamplePath)
+		sampleEnv, err := r.SampleEnv()
 		if err != nil {
 			return false, err
 		}
@@ -86,8 +85,9 @@ func UpdateTag(pattern, image, tag, recipeName string) (bool, error) {
 }
 
 // UpdateLabel updates a label in-place on file system local compose files.
-func UpdateLabel(pattern, serviceName, label, recipeName string) error {
-	composeFiles, err := filepath.Glob(pattern)
+func (r Recipe2) UpdateLabel(pattern, serviceName, label string) error {
+	fullPattern := fmt.Sprintf("%s/%s", r.Dir, pattern)
+	composeFiles, err := filepath.Glob(fullPattern)
 	if err != nil {
 		return err
 	}
@@ -97,8 +97,7 @@ func UpdateLabel(pattern, serviceName, label, recipeName string) error {
 	for _, composeFile := range composeFiles {
 		opts := stack.Deploy{Composefiles: []string{composeFile}}
 
-		envSamplePath := path.Join(config.RECIPES_DIR, recipeName, ".env.sample")
-		sampleEnv, err := envfile.ReadEnv(envSamplePath)
+		sampleEnv, err := r.SampleEnv()
 		if err != nil {
 			return err
 		}
diff --git a/pkg/recipe/files.go b/pkg/recipe/files.go
index 6177458f43..8bc76b25f7 100644
--- a/pkg/recipe/files.go
+++ b/pkg/recipe/files.go
@@ -2,15 +2,12 @@ package recipe
 
 import (
 	"fmt"
-	"path"
 
-	"coopcloud.tech/abra/pkg/config"
 	"coopcloud.tech/abra/pkg/envfile"
 )
 
 func (r Recipe2) SampleEnv() (map[string]string, error) {
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	sampleEnv, err := envfile.ReadEnv(envSamplePath)
+	sampleEnv, err := envfile.ReadEnv(r.SampleEnvPath)
 	if err != nil {
 		return sampleEnv, fmt.Errorf("unable to discover .env.sample for %s", r.Name)
 	}
diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go
index c4ac38d4b8..7f23b7c16c 100644
--- a/pkg/recipe/recipe.go
+++ b/pkg/recipe/recipe.go
@@ -13,9 +13,7 @@ import (
 	"strings"
 
 	"coopcloud.tech/abra/pkg/catalogue"
-	"coopcloud.tech/abra/pkg/compose"
 	"coopcloud.tech/abra/pkg/config"
-	"coopcloud.tech/abra/pkg/envfile"
 	"coopcloud.tech/abra/pkg/formatter"
 	gitPkg "coopcloud.tech/abra/pkg/git"
 	"coopcloud.tech/abra/pkg/limit"
@@ -143,29 +141,6 @@ func (r Recipe) Dir() string {
 	return path.Join(config.RECIPES_DIR, r.Name)
 }
 
-// UpdateLabel updates a recipe label
-func (r Recipe) UpdateLabel(pattern, serviceName, label string) error {
-	fullPattern := fmt.Sprintf("%s/%s/%s", config.RECIPES_DIR, r.Name, pattern)
-	if err := compose.UpdateLabel(fullPattern, serviceName, label, r.Name); err != nil {
-		return err
-	}
-	return nil
-}
-
-// UpdateTag updates a recipe tag
-func (r Recipe) UpdateTag(image, tag string) (bool, error) {
-	pattern := fmt.Sprintf("%s/%s/compose**yml", config.RECIPES_DIR, r.Name)
-
-	image = formatter.StripTagMeta(image)
-
-	ok, err := compose.UpdateTag(pattern, image, tag, r.Name)
-	if err != nil {
-		return false, err
-	}
-
-	return ok, nil
-}
-
 // Tags list the recipe tags
 func (r Recipe) Tags() ([]string, error) {
 	var tags []string
@@ -209,8 +184,7 @@ func Get(recipeName string, offline bool) (Recipe, error) {
 		return Recipe{}, fmt.Errorf("%s is missing a compose.yml or compose.*.yml file?", r.Name)
 	}
 
-	envSamplePath := path.Join(config.RECIPES_DIR, r.Name, ".env.sample")
-	sampleEnv, err := envfile.ReadEnv(envSamplePath)
+	sampleEnv, err := r.SampleEnv()
 	if err != nil {
 		return Recipe{}, err
 	}