From d7a870b887d7470ed8110184ffffe86973db154b Mon Sep 17 00:00:00 2001 From: p4u1 Date: Mon, 8 Jul 2024 15:13:13 +0200 Subject: [PATCH 1/4] feat: remote recipes --- cli/app/cmd.go | 10 +++----- cli/app/ps.go | 7 +++--- cli/recipe/release.go | 3 +-- cli/updater/updater.go | 3 +-- pkg/app/app_test.go | 6 ++--- pkg/envfile/envfile_test.go | 9 +++---- pkg/lint/recipe.go | 16 ++++-------- pkg/recipe/git.go | 27 ++++++++------------ pkg/recipe/recipe.go | 29 ++++++++++++++++++++-- pkg/recipe/recipe_test.go | 49 +++++++++++++++++++++++++++++++++++++ pkg/secret/secret.go | 4 +-- 11 files changed, 107 insertions(+), 56 deletions(-) diff --git a/cli/app/cmd.go b/cli/app/cmd.go index 9a09d6ad..58ec5dc3 100644 --- a/cli/app/cmd.go +++ b/cli/app/cmd.go @@ -14,7 +14,6 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/recipe" "github.com/urfave/cli" ) @@ -209,24 +208,23 @@ var appCmdListCommand = cli.Command{ Before: internal.SubCommandBefore, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) - r := recipe.Get(app.Recipe.Name) - if err := r.EnsureExists(); err != nil { + if err := app.Recipe.EnsureExists(); err != nil { log.Fatal(err) } if !internal.Chaos { - if err := r.EnsureIsClean(); err != nil { + if err := app.Recipe.EnsureIsClean(); err != nil { log.Fatal(err) } if !internal.Offline { - if err := r.EnsureUpToDate(); err != nil { + if err := app.Recipe.EnsureUpToDate(); err != nil { log.Fatal(err) } } - if err := r.EnsureLatest(); err != nil { + if err := app.Recipe.EnsureLatest(); err != nil { log.Fatal(err) } } diff --git a/cli/app/ps.go b/cli/app/ps.go index 537c4018..008d59ce 100644 --- a/cli/app/ps.go +++ b/cli/app/ps.go @@ -11,7 +11,6 @@ import ( "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/recipe" abraService "coopcloud.tech/abra/pkg/service" stack "coopcloud.tech/abra/pkg/upstream/stack" dockerFormatter "github.com/docker/cli/cli/command/formatter" @@ -35,6 +34,9 @@ var appPsCommand = cli.Command{ BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + if err := app.Recipe.Ensure(false, false); err != nil { + log.Fatal(err) + } cl, err := client.New(app.Server) if err != nil { @@ -74,8 +76,7 @@ var appPsCommand = cli.Command{ // showPSOutput renders ps output. func showPSOutput(app appPkg.App, cl *dockerClient.Client, deployedVersion, chaosVersion string) { - r := recipe.Get(app.Recipe.Name) - composeFiles, err := r.GetComposeFiles(app.Env) + composeFiles, err := app.Recipe.GetComposeFiles(app.Env) if err != nil { log.Fatal(err) return diff --git a/cli/recipe/release.go b/cli/recipe/release.go index 2bb17d70..ad867040 100644 --- a/cli/recipe/release.go +++ b/cli/recipe/release.go @@ -10,7 +10,6 @@ import ( "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" - "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/formatter" gitPkg "coopcloud.tech/abra/pkg/git" "coopcloud.tech/abra/pkg/log" @@ -391,7 +390,7 @@ func pushRelease(recipe recipe.Recipe, tagString string) error { if err := recipe.Push(internal.Dry); err != nil { return err } - url := fmt.Sprintf("%s/%s/src/tag/%s", config.REPOS_BASE_URL, recipe.Name, tagString) + url := fmt.Sprintf("%s/src/tag/%s", recipe.GitURL, tagString) log.Infof("new release published: %s", url) } else { log.Info("no -p/--publish passed, not publishing") diff --git a/cli/updater/updater.go b/cli/updater/updater.go index fd2d321e..ca2b8ff2 100644 --- a/cli/updater/updater.go +++ b/cli/updater/updater.go @@ -433,8 +433,7 @@ func tryUpgrade(cl *dockerclient.Client, stackName, recipeName string) error { } // upgrade performs all necessary steps to upgrade an app. -func upgrade(cl *dockerclient.Client, stackName, recipeName, - upgradeVersion string) error { +func upgrade(cl *dockerclient.Client, stackName, recipeName, upgradeVersion string) error { env, err := getEnv(cl, stackName) if err != nil { return err diff --git a/pkg/app/app_test.go b/pkg/app/app_test.go index a10cc753..3eb2d7dd 100644 --- a/pkg/app/app_test.go +++ b/pkg/app/app_test.go @@ -85,8 +85,7 @@ func TestGetComposeFiles(t *testing.T) { } for _, test := range tests { - r2 := recipe.Get(r.Name) - composeFiles, err := r2.GetComposeFiles(test.appEnv) + composeFiles, err := r.GetComposeFiles(test.appEnv) if err != nil { t.Fatal(err) } @@ -107,8 +106,7 @@ func TestGetComposeFilesError(t *testing.T) { } for _, test := range tests { - r2 := recipe.Get(r.Name) - _, err := r2.GetComposeFiles(test.appEnv) + _, err := r.GetComposeFiles(test.appEnv) if err == nil { t.Fatalf("should have failed: %v", test.appEnv) } diff --git a/pkg/envfile/envfile_test.go b/pkg/envfile/envfile_test.go index 3c15555b..ffab3ee4 100644 --- a/pkg/envfile/envfile_test.go +++ b/pkg/envfile/envfile_test.go @@ -113,8 +113,7 @@ func TestCheckEnv(t *testing.T) { t.Fatal(err) } - r2 := recipe.Get(r.Name) - envSample, err := r2.SampleEnv() + envSample, err := r.SampleEnv() if err != nil { t.Fatal(err) } @@ -147,8 +146,7 @@ func TestCheckEnvError(t *testing.T) { t.Fatal(err) } - r2 := recipe.Get(r.Name) - envSample, err := r2.SampleEnv() + envSample, err := r.SampleEnv() if err != nil { t.Fatal(err) } @@ -183,8 +181,7 @@ func TestEnvVarCommentsRemoved(t *testing.T) { t.Fatal(err) } - r2 := recipe.Get(r.Name) - envSample, err := r2.SampleEnv() + envSample, err := r.SampleEnv() if err != nil { t.Fatal(err) } diff --git a/pkg/lint/recipe.go b/pkg/lint/recipe.go index 7c7c6ac9..51ee392f 100644 --- a/pkg/lint/recipe.go +++ b/pkg/lint/recipe.go @@ -6,7 +6,6 @@ import ( "os" "path" - "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/log" "coopcloud.tech/abra/pkg/recipe" recipePkg "coopcloud.tech/abra/pkg/recipe" @@ -214,8 +213,7 @@ func LintComposeVersion(recipe recipe.Recipe) (bool, error) { } func LintEnvConfigPresent(r recipe.Recipe) (bool, error) { - r2 := recipe.Get(r.Name) - if _, err := os.Stat(r2.SampleEnvPath); !os.IsNotExist(err) { + if _, err := os.Stat(r.SampleEnvPath); !os.IsNotExist(err) { return true, nil } @@ -241,10 +239,9 @@ func LintAppService(recipe recipe.Recipe) (bool, error) { // the recipe. This typically means that no domain is required to deploy and // therefore no matching traefik deploy label will be present. func LintTraefikEnabledSkipCondition(r recipe.Recipe) (bool, error) { - r2 := recipe.Get(r.Name) - sampleEnv, err := r2.SampleEnv() + sampleEnv, err := r.SampleEnv() if err != nil { - return false, fmt.Errorf("Unable to discover .env.sample for %s", r2.Name) + return false, fmt.Errorf("Unable to discover .env.sample for %s", r.Name) } if _, ok := sampleEnv["DOMAIN"]; !ok { @@ -390,8 +387,7 @@ func LintHasPublishedVersion(recipe recipe.Recipe) (bool, error) { } func LintMetadataFilledIn(r recipe.Recipe) (bool, error) { - r2 := recipe.Get(r.Name) - features, category, err := recipe.GetRecipeFeaturesAndCategory(r2) + features, category, err := recipe.GetRecipeFeaturesAndCategory(r) if err != nil { return false, err } @@ -431,9 +427,7 @@ func LintAbraShVendors(recipe recipe.Recipe) (bool, error) { } func LintHasRecipeRepo(recipe recipe.Recipe) (bool, error) { - url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, recipe.Name) - - res, err := http.Get(url) + res, err := http.Get(recipe.GitURL) if err != nil { return false, err } diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index 95cb87c9..851e3229 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -39,17 +39,14 @@ func (r Recipe) Ensure(chaos bool, offline bool) error { // EnsureExists ensures that the recipe is locally cloned func (r Recipe) EnsureExists() error { - recipeDir := path.Join(config.RECIPES_DIR, r.Name) - - if _, err := os.Stat(recipeDir); os.IsNotExist(err) { - log.Debugf("%s does not exist, attemmpting to clone", recipeDir) - url := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, r.Name) - if err := gitPkg.Clone(recipeDir, url); err != nil { + if _, err := os.Stat(r.Dir); os.IsNotExist(err) { + log.Debugf("%s does not exist, attemmpting to clone", r.Dir) + if err := gitPkg.Clone(r.Dir, r.GitURL); err != nil { return err } } - if err := gitPkg.EnsureGitRepo(recipeDir); err != nil { + if err := gitPkg.EnsureGitRepo(r.Dir); err != nil { return err } @@ -60,13 +57,11 @@ func (r Recipe) EnsureExists() error { func (r Recipe) EnsureVersion(version string) (bool, error) { isChaosCommit := false - recipeDir := path.Join(config.RECIPES_DIR, r.Name) - - if err := gitPkg.EnsureGitRepo(recipeDir); err != nil { + if err := gitPkg.EnsureGitRepo(r.Dir); err != nil { return isChaosCommit, err } - repo, err := git.PlainOpen(recipeDir) + repo, err := git.PlainOpen(r.Dir) if err != nil { return isChaosCommit, err } @@ -117,23 +112,21 @@ func (r Recipe) EnsureVersion(version string) (bool, error) { return isChaosCommit, nil } - log.Debugf("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), recipeDir) + log.Debugf("successfully checked %s out to %s in %s", r.Name, tagRef.Short(), r.Dir) return isChaosCommit, nil } // EnsureIsClean makes sure that the recipe repository has no unstaged changes. func (r Recipe) EnsureIsClean() error { - recipeDir := path.Join(config.RECIPES_DIR, r.Name) - - isClean, err := gitPkg.IsClean(recipeDir) + isClean, err := gitPkg.IsClean(r.Dir) if err != nil { - return fmt.Errorf("unable to check git clean status in %s: %s", recipeDir, err) + return fmt.Errorf("unable to check git clean status in %s: %s", r.Dir, err) } if !isClean { msg := "%s (%s) has locally unstaged changes? please commit/remove your changes before proceeding" - return fmt.Errorf(msg, r.Name, recipeDir) + return fmt.Errorf(msg, r.Name, r.Dir) } return nil diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index 5a33dabc..a68a9c53 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io/ioutil" + "net/url" "os" "path" "slices" @@ -123,11 +124,28 @@ type Features struct { } func Get(name string) Recipe { - dir := path.Join(config.RECIPES_DIR, name) + gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, name) + sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, name) + if strings.Contains(name, "/") { + u, err := url.Parse(name) + if err != nil { + log.Fatalf("invalid recipe: %s", err) + } + u.Scheme = "https" + gitURL = u.String() + ".git" + + u.Scheme = "ssh" + u.User = url.User("git") + sshURL = u.String() + ".git" + } + + dir := path.Join(config.RECIPES_DIR, escapeRecipeName(name)) + return Recipe{ Name: name, Dir: dir, - SSHURL: fmt.Sprintf(config.SSH_URL_TEMPLATE, name), + GitURL: gitURL, + SSHURL: sshURL, ComposePath: path.Join(dir, "compose.yml"), ReadmePath: path.Join(dir, "README.md"), @@ -139,6 +157,7 @@ func Get(name string) Recipe { type Recipe struct { Name string Dir string + GitURL string SSHURL string ComposePath string @@ -147,6 +166,12 @@ type Recipe struct { AbraShPath string } +func escapeRecipeName(recipeName string) string { + recipeName = strings.ReplaceAll(recipeName, "/", "_") + recipeName = strings.ReplaceAll(recipeName, ".", "_") + return recipeName +} + // GetRecipesLocal retrieves all local recipe directories func GetRecipesLocal() ([]string, error) { var recipes []string diff --git a/pkg/recipe/recipe_test.go b/pkg/recipe/recipe_test.go index f73d613b..7837cde3 100644 --- a/pkg/recipe/recipe_test.go +++ b/pkg/recipe/recipe_test.go @@ -1,11 +1,60 @@ package recipe import ( + "path" "testing" + "coopcloud.tech/abra/pkg/config" + + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" ) +func TestGet(t *testing.T) { + cfg := config.LoadAbraConfig() + testcases := []struct { + name string + recipe Recipe + }{ + { + name: "foo", + recipe: Recipe{ + Name: "foo", + Dir: path.Join(cfg.GetAbraDir(), "/recipes/foo"), + GitURL: "https://git.coopcloud.tech/coop-cloud/foo.git", + SSHURL: "ssh://git@git.coopcloud.tech:2222/coop-cloud/foo.git", + ComposePath: path.Join(cfg.GetAbraDir(), "recipes/foo/compose.yml"), + ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/foo/README.md"), + SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/foo/.env.sample"), + AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/foo/abra.sh"), + }, + }, + { + name: "mygit.org/myorg/cool-recipe", + recipe: Recipe{ + Name: "mygit.org/myorg/cool-recipe", + Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"), + GitURL: "https://mygit.org/myorg/cool-recipe.git", + SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git", + ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"), + ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"), + SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"), + AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"), + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + t.Setenv("ABRA_DIR", "") + recipe := Get(tc.name) + if diff := cmp.Diff(tc.recipe, recipe); diff != "" { + t.Errorf("Recipe mismatch (-want +got):\n%s", diff) + } + }) + } +} + func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) { offline := true diff --git a/pkg/secret/secret.go b/pkg/secret/secret.go index 63e3efdb..1f17169c 100644 --- a/pkg/secret/secret.go +++ b/pkg/secret/secret.go @@ -16,7 +16,6 @@ import ( "coopcloud.tech/abra/pkg/config" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/upstream/stack" loader "coopcloud.tech/abra/pkg/upstream/stack" "github.com/decentral1se/passgen" @@ -246,8 +245,7 @@ type secretStatuses []secretStatus func PollSecretsStatus(cl *dockerClient.Client, app appPkg.App) (secretStatuses, error) { var secStats secretStatuses - r := recipe.Get(app.Recipe.Name) - composeFiles, err := r.GetComposeFiles(app.Env) + composeFiles, err := app.Recipe.GetComposeFiles(app.Env) if err != nil { return secStats, err } From 790dbca362a31777f2d986d050882447ec525438 Mon Sep 17 00:00:00 2001 From: p4u1 Date: Mon, 8 Jul 2024 16:20:38 +0200 Subject: [PATCH 2/4] feat!: remove all catalogue reads from app commands --- cli/app/deploy.go | 20 +------------------- cli/app/list.go | 9 +-------- cli/app/new.go | 2 +- cli/app/rollback.go | 22 +--------------------- cli/app/upgrade.go | 21 +-------------------- cli/catalogue/catalogue.go | 2 +- cli/recipe/version.go | 2 +- pkg/recipe/git.go | 2 +- 8 files changed, 8 insertions(+), 72 deletions(-) diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 46249174..aa4f3367 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -15,7 +15,6 @@ import ( "coopcloud.tech/abra/pkg/git" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/upstream/stack" "github.com/urfave/cli" ) @@ -121,27 +120,10 @@ EXAMPLE: } if !internal.Chaos && specificVersion == "" { - catl, err := recipe.ReadRecipeCatalogue(internal.Offline) + versions, err := app.Recipe.Tags() if err != nil { log.Fatal(err) } - versions, err := recipe.GetRecipeCatalogueVersions(app.Recipe.Name, catl) - if err != nil { - log.Fatal(err) - } - - if len(versions) == 0 && !internal.Chaos { - log.Debug("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) - if err != nil { - log.Fatal(err) - } - for _, recipeVersion := range recipeVersions { - for version := range recipeVersion { - versions = append(versions, version) - } - } - } if len(versions) > 0 && !internal.Chaos { version = versions[len(versions)-1] diff --git a/cli/app/list.go b/cli/app/list.go index 407a39cb..20fa2acc 100644 --- a/cli/app/list.go +++ b/cli/app/list.go @@ -11,7 +11,6 @@ import ( appPkg "coopcloud.tech/abra/pkg/app" "coopcloud.tech/abra/pkg/formatter" "coopcloud.tech/abra/pkg/log" - "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/tagcmp" "github.com/urfave/cli" ) @@ -101,7 +100,6 @@ can take some time.`, sort.Sort(appPkg.ByServerAndRecipe(apps)) statuses := make(map[string]map[string]string) - var catl recipe.RecipeCatalogue if status { alreadySeen := make(map[string]bool) for _, app := range apps { @@ -114,11 +112,6 @@ can take some time.`, if err != nil { log.Fatal(err) } - - catl, err = recipe.ReadRecipeCatalogue(internal.Offline) - if err != nil { - log.Fatal(err) - } } var totalServersCount int @@ -182,7 +175,7 @@ can take some time.`, var newUpdates []string if version != "unknown" { - updates, err := recipe.GetRecipeCatalogueVersions(app.Recipe.Name, catl) + updates, err := app.Recipe.Tags() if err != nil { log.Fatal(err) } diff --git a/cli/app/new.go b/cli/app/new.go index 2ef010bc..9a81c581 100644 --- a/cli/app/new.go +++ b/cli/app/new.go @@ -78,7 +78,7 @@ var appNewCommand = cli.Command{ if c.Args().Get(1) == "" { var version string - recipeVersions, err := recipe.GetRecipeVersions(internal.Offline) + recipeVersions, err := recipe.GetRecipeVersions() if err != nil { log.Fatal(err) } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index 91a4e6dd..366d3127 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -8,7 +8,6 @@ import ( "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/lint" - "coopcloud.tech/abra/pkg/recipe" stack "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" @@ -75,30 +74,11 @@ EXAMPLE: log.Fatalf("%s is not deployed?", app.Name) } - catl, err := recipe.ReadRecipeCatalogue(internal.Offline) + versions, err := app.Recipe.Tags() if err != nil { log.Fatal(err) } - versions, err := recipe.GetRecipeCatalogueVersions(app.Recipe.Name, catl) - if err != nil { - log.Fatal(err) - } - - if len(versions) == 0 { - log.Debug("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) - if err != nil { - log.Fatal(err) - } - - for _, recipeVersion := range recipeVersions { - for version := range recipeVersion { - versions = append(versions, version) - } - } - } - var availableDowngrades []string if deployMeta.Version == "unknown" { availableDowngrades = versions diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 54b79730..69198151 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -11,7 +11,6 @@ import ( "coopcloud.tech/abra/pkg/envfile" "coopcloud.tech/abra/pkg/lint" "coopcloud.tech/abra/pkg/log" - recipePkg "coopcloud.tech/abra/pkg/recipe" stack "coopcloud.tech/abra/pkg/upstream/stack" "coopcloud.tech/tagcmp" "github.com/AlecAivazis/survey/v2" @@ -75,29 +74,11 @@ EXAMPLE: log.Fatalf("%s is not deployed?", app.Name) } - catl, err := recipePkg.ReadRecipeCatalogue(internal.Offline) + versions, err := app.Recipe.Tags() if err != nil { log.Fatal(err) } - versions, err := recipePkg.GetRecipeCatalogueVersions(app.Recipe.Name, catl) - if err != nil { - log.Fatal(err) - } - - if len(versions) == 0 { - log.Debug("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := app.Recipe.GetRecipeVersions(internal.Offline) - if err != nil { - log.Fatal(err) - } - for _, recipeVersion := range recipeVersions { - for version := range recipeVersion { - versions = append(versions, version) - } - } - } - var availableUpgrades []string if deployMeta.Version == "unknown" { availableUpgrades = versions diff --git a/cli/catalogue/catalogue.go b/cli/catalogue/catalogue.go index bcfe3505..27069680 100644 --- a/cli/catalogue/catalogue.go +++ b/cli/catalogue/catalogue.go @@ -91,7 +91,7 @@ keys configured on your account.`, continue } - versions, err := r.GetRecipeVersions(internal.Offline) + versions, err := r.GetRecipeVersions() if err != nil { log.Warn(err) } diff --git a/cli/recipe/version.go b/cli/recipe/version.go index 74efbd34..a0069bc0 100644 --- a/cli/recipe/version.go +++ b/cli/recipe/version.go @@ -48,7 +48,7 @@ var recipeVersionCommand = cli.Command{ if !ok { log.Warn("no published versions in catalogue, trying local recipe repository") - recipeVersions, err := recipe.GetRecipeVersions(internal.Offline) + recipeVersions, err := recipe.GetRecipeVersions() if err != nil { log.Warn(err) } diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index 851e3229..775c7491 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -292,7 +292,7 @@ func (r Recipe) Tags() ([]string, error) { } // GetRecipeVersions retrieves all recipe versions. -func (r Recipe) GetRecipeVersions(offline bool) (RecipeVersions, error) { +func (r Recipe) GetRecipeVersions() (RecipeVersions, error) { versions := RecipeVersions{} log.Debugf("attempting to open git repository in %s", r.Dir) From 4085eb6654511cc53028f30828fe7f55c449e98c Mon Sep 17 00:00:00 2001 From: p4u1 Date: Mon, 8 Jul 2024 17:11:15 +0200 Subject: [PATCH 3/4] feat: define recipe version inside app env file --- cli/app/cp.go | 4 ++++ cli/app/deploy.go | 3 +++ cli/app/restart.go | 3 +++ cli/app/restore.go | 1 - cli/app/rollback.go | 3 +++ cli/app/secret.go | 39 ++------------------------------------- cli/app/services.go | 3 +++ cli/app/undeploy.go | 3 +++ pkg/recipe/git.go | 22 +++++++++++++++------- pkg/recipe/recipe.go | 25 +++++++++++++++++-------- pkg/recipe/recipe_test.go | 28 ++++++++++++++++++++++++++++ 11 files changed, 81 insertions(+), 53 deletions(-) diff --git a/cli/app/cp.go b/cli/app/cp.go index f1c50d2a..e5d2035c 100644 --- a/cli/app/cp.go +++ b/cli/app/cp.go @@ -50,6 +50,10 @@ And if you want to copy that file back to your current working directory locally Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + log.Fatal(err) + } + src := c.Args().Get(1) dst := c.Args().Get(2) if src == "" { diff --git a/cli/app/deploy.go b/cli/app/deploy.go index aa4f3367..9a9524f1 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -52,6 +52,9 @@ EXAMPLE: stackName := app.StackName() specificVersion := c.Args().Get(1) + if specificVersion == "" { + specificVersion = app.Recipe.Version + } if specificVersion != "" && internal.Chaos { log.Fatal("cannot use and --chaos together") } diff --git a/cli/app/restart.go b/cli/app/restart.go index 1acf7ca2..bba2983f 100644 --- a/cli/app/restart.go +++ b/cli/app/restart.go @@ -39,6 +39,9 @@ EXAMPLE: BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + if err := app.Recipe.Ensure(false, false); err != nil { + log.Fatal(err) + } serviceName := c.Args().Get(1) if serviceName == "" && !internal.AllServices { diff --git a/cli/app/restore.go b/cli/app/restore.go index b31cc097..8ce54fb8 100644 --- a/cli/app/restore.go +++ b/cli/app/restore.go @@ -31,7 +31,6 @@ var appRestoreCommand = cli.Command{ BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index 366d3127..8ec65d06 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -86,6 +86,9 @@ EXAMPLE: } specificVersion := c.Args().Get(1) + if specificVersion == "" { + specificVersion = app.Recipe.Version + } if specificVersion != "" { parsedDeployedVersion, err := tagcmp.Parse(deployMeta.Version) if err != nil { diff --git a/cli/app/secret.go b/cli/app/secret.go index 28554979..a8fe9486 100644 --- a/cli/app/secret.go +++ b/cli/app/secret.go @@ -55,7 +55,6 @@ var appSecretGenerateCommand = cli.Command{ BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) - if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } @@ -246,27 +245,10 @@ Example: `, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) - - if err := app.Recipe.EnsureExists(); err != nil { + if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } - if !internal.Chaos { - if err := app.Recipe.EnsureIsClean(); err != nil { - log.Fatal(err) - } - - if !internal.Offline { - if err := app.Recipe.EnsureUpToDate(); err != nil { - log.Fatal(err) - } - } - - if err := app.Recipe.EnsureLatest(); err != nil { - log.Fatal(err) - } - } - composeFiles, err := app.Recipe.GetComposeFiles(app.Env) if err != nil { log.Fatal(err) @@ -354,27 +336,10 @@ var appSecretLsCommand = cli.Command{ BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) - - if err := app.Recipe.EnsureExists(); err != nil { + if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { log.Fatal(err) } - if !internal.Chaos { - if err := app.Recipe.EnsureIsClean(); err != nil { - log.Fatal(err) - } - - if !internal.Offline { - if err := app.Recipe.EnsureUpToDate(); err != nil { - log.Fatal(err) - } - } - - if err := app.Recipe.EnsureLatest(); err != nil { - log.Fatal(err) - } - } - cl, err := client.New(app.Server) if err != nil { log.Fatal(err) diff --git a/cli/app/services.go b/cli/app/services.go index 1986f6e7..97dfda61 100644 --- a/cli/app/services.go +++ b/cli/app/services.go @@ -28,6 +28,9 @@ var appServicesCommand = cli.Command{ BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + log.Fatal(err) + } cl, err := client.New(app.Server) if err != nil { diff --git a/cli/app/undeploy.go b/cli/app/undeploy.go index 342532a3..5e63d62c 100644 --- a/cli/app/undeploy.go +++ b/cli/app/undeploy.go @@ -81,6 +81,9 @@ any previously attached volumes as eligible for pruning once undeployed. Passing "-p/--prune" does not remove those volumes.`, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) + if err := app.Recipe.Ensure(internal.Chaos, internal.Offline); err != nil { + log.Fatal(err) + } stackName := app.StackName() cl, err := client.New(app.Server) diff --git a/pkg/recipe/git.go b/pkg/recipe/git.go index 775c7491..4978856d 100644 --- a/pkg/recipe/git.go +++ b/pkg/recipe/git.go @@ -21,15 +21,23 @@ func (r Recipe) Ensure(chaos bool, offline bool) error { return err } - if !chaos { - if err := r.EnsureIsClean(); err != nil { + if chaos { + return nil + } + + if err := r.EnsureIsClean(); err != nil { + return err + } + if !offline { + if err := r.EnsureUpToDate(); err != nil { + log.Fatal(err) + } + } + if r.Version != "" { + if _, err := r.EnsureVersion(r.Version); err != nil { return err } - if !offline { - if err := r.EnsureUpToDate(); err != nil { - log.Fatal(err) - } - } + } else { if err := r.EnsureLatest(); err != nil { return err } diff --git a/pkg/recipe/recipe.go b/pkg/recipe/recipe.go index a68a9c53..df8134f3 100644 --- a/pkg/recipe/recipe.go +++ b/pkg/recipe/recipe.go @@ -124,6 +124,13 @@ type Features struct { } func Get(name string) Recipe { + version := "" + if strings.Contains(name, ":") { + split := strings.Split(name, ":") + name = split[0] + version = split[1] + } + gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, name) sshURL := fmt.Sprintf(config.SSH_URL_TEMPLATE, name) if strings.Contains(name, "/") { @@ -142,10 +149,11 @@ func Get(name string) Recipe { dir := path.Join(config.RECIPES_DIR, escapeRecipeName(name)) return Recipe{ - Name: name, - Dir: dir, - GitURL: gitURL, - SSHURL: sshURL, + Name: name, + Version: version, + Dir: dir, + GitURL: gitURL, + SSHURL: sshURL, ComposePath: path.Join(dir, "compose.yml"), ReadmePath: path.Join(dir, "README.md"), @@ -155,10 +163,11 @@ func Get(name string) Recipe { } type Recipe struct { - Name string - Dir string - GitURL string - SSHURL string + Name string + Version string + Dir string + GitURL string + SSHURL string ComposePath string ReadmePath string diff --git a/pkg/recipe/recipe_test.go b/pkg/recipe/recipe_test.go index 7837cde3..053361df 100644 --- a/pkg/recipe/recipe_test.go +++ b/pkg/recipe/recipe_test.go @@ -29,6 +29,20 @@ func TestGet(t *testing.T) { AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/foo/abra.sh"), }, }, + { + name: "foo:1.2.3", + recipe: Recipe{ + Name: "foo", + Version: "1.2.3", + Dir: path.Join(cfg.GetAbraDir(), "/recipes/foo"), + GitURL: "https://git.coopcloud.tech/coop-cloud/foo.git", + SSHURL: "ssh://git@git.coopcloud.tech:2222/coop-cloud/foo.git", + ComposePath: path.Join(cfg.GetAbraDir(), "recipes/foo/compose.yml"), + ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/foo/README.md"), + SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/foo/.env.sample"), + AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/foo/abra.sh"), + }, + }, { name: "mygit.org/myorg/cool-recipe", recipe: Recipe{ @@ -42,6 +56,20 @@ func TestGet(t *testing.T) { AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"), }, }, + { + name: "mygit.org/myorg/cool-recipe:1.2.4", + recipe: Recipe{ + Name: "mygit.org/myorg/cool-recipe", + Version: "1.2.4", + Dir: path.Join(cfg.GetAbraDir(), "/recipes/mygit_org_myorg_cool-recipe"), + GitURL: "https://mygit.org/myorg/cool-recipe.git", + SSHURL: "ssh://git@mygit.org/myorg/cool-recipe.git", + ComposePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/compose.yml"), + ReadmePath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/README.md"), + SampleEnvPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/.env.sample"), + AbraShPath: path.Join(cfg.GetAbraDir(), "recipes/mygit_org_myorg_cool-recipe/abra.sh"), + }, + }, } for _, tc := range testcases { From 7596982282e39629e97314d3373c4111d13d5133 Mon Sep 17 00:00:00 2001 From: p4u1 Date: Mon, 8 Jul 2024 17:55:40 +0200 Subject: [PATCH 4/4] feat: update new version in env file --- cli/app/deploy.go | 7 +++++++ cli/app/rollback.go | 7 +++++++ cli/app/upgrade.go | 7 +++++++ pkg/app/app.go | 27 +++++++++++++++++++++++++++ 4 files changed, 48 insertions(+) diff --git a/cli/app/deploy.go b/cli/app/deploy.go index 9a9524f1..0f2c0ed5 100644 --- a/cli/app/deploy.go +++ b/cli/app/deploy.go @@ -240,6 +240,13 @@ EXAMPLE: log.Fatalf("attempting to run post deploy commands, saw: %s", err) } } + + if app.Recipe.Version != "" && specificVersion != "" && specificVersion != app.Recipe.Version { + err := app.WriteRecipeVersion(specificVersion) + if err != nil { + log.Fatalf("writing new recipe version in env file: %s", err) + } + } return nil }, } diff --git a/cli/app/rollback.go b/cli/app/rollback.go index 8ec65d06..79a35359 100644 --- a/cli/app/rollback.go +++ b/cli/app/rollback.go @@ -211,6 +211,13 @@ EXAMPLE: log.Fatal(err) } + if app.Recipe.Version != "" { + err := app.WriteRecipeVersion(chosenDowngrade) + if err != nil { + log.Fatalf("writing new recipe version in env file: %s", err) + } + } + return nil }, } diff --git a/cli/app/upgrade.go b/cli/app/upgrade.go index 69198151..26366aef 100644 --- a/cli/app/upgrade.go +++ b/cli/app/upgrade.go @@ -267,6 +267,13 @@ EXAMPLE: } } + if app.Recipe.Version != "" { + err := app.WriteRecipeVersion(chosenUpgrade) + if err != nil { + log.Fatalf("writing new recipe version in env file: %s", err) + } + } + return nil }, } diff --git a/pkg/app/app.go b/pkg/app/app.go index 9b09dcd9..95b6d191 100644 --- a/pkg/app/app.go +++ b/pkg/app/app.go @@ -568,3 +568,30 @@ func ReadAbraShCmdNames(abraSh string) ([]string, error) { return cmdNames, nil } + +func (a App) WriteRecipeVersion(version string) error { + file, err := os.Open(a.Path) + if err != nil { + return err + } + defer file.Close() + + scanner := bufio.NewScanner(file) + lines := []string{} + for scanner.Scan() { + line := scanner.Text() + if !strings.Contains(line, "RECIPE=") && !strings.Contains(line, "TYPE") { + lines = append(lines, line) + continue + } + splitted := strings.Split(line, ":") + line = fmt.Sprintf("%s:%s", splitted[0], version) + lines = append(lines, line) + } + + if err := scanner.Err(); err != nil { + log.Fatal(err) + } + + return os.WriteFile(a.Path, []byte(strings.Join(lines, "\n")), os.ModePerm) +}