Skip to content

Commit

Permalink
Restructure check command
Browse files Browse the repository at this point in the history
  • Loading branch information
majori committed Nov 8, 2023
1 parent be76dcd commit 3d715f7
Show file tree
Hide file tree
Showing 13 changed files with 200 additions and 126 deletions.
70 changes: 67 additions & 3 deletions features/check-recipes.feature
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,49 @@ Feature: Check for new recipe versions
When I change recipe "foo" to version "v0.0.2"
And I push the recipe "foo" to the local OCI repository
And I check new versions for recipe "foo"
Then CLI produced an output "New versions found"
Then CLI produced an output "new versions found: v0.0.2"

Scenario: Find multiple newer version for a recipe
Given a project directory
And a recipes directory
And a recipe "foo" that generates file "README.md"
And a local OCI registry
When I execute recipe "foo"
And the source of the sauce with recipe "foo" is in the local OCI registry
Then execution of the recipe has succeeded
When I push the recipe "foo" to the local OCI repository
Then no errors were printed
When I change recipe "foo" to version "v0.0.2"
And I push the recipe "foo" to the local OCI repository
And I change recipe "foo" to version "v0.0.3"
And I push the recipe "foo" to the local OCI repository
Then I check new versions for recipe "foo"
Then CLI produced an output "new versions found: v0.0.2, v0.0.3"

Scenario: Find newer version for multiple recipes
Given a project directory
And a recipes directory
And a recipe "foo" that generates file "foo.md"
And a recipe "bar" that generates file "bar.md"
And a local OCI registry
When I execute recipe "foo"
And the source of the sauce with recipe "foo" is in the local OCI registry
Then execution of the recipe has succeeded
When I execute recipe "bar"
And the source of the sauce with recipe "bar" is in the local OCI registry
Then execution of the recipe has succeeded
When I push the recipe "foo" to the local OCI repository
When I push the recipe "bar" to the local OCI repository
Then no errors were printed
When I change recipe "foo" to version "v0.0.2"
And I push the recipe "foo" to the local OCI repository
Then no errors were printed
When I change recipe "bar" to version "v0.0.2"
And I push the recipe "bar" to the local OCI repository
Then no errors were printed
Then I check new versions
Then CLI produced an output "foo: new versions found: v0.0.2"
And CLI produced an output "bar: new versions found: v0.0.2"

Scenario: Unable to find newer recipe versions
Given a project directory
Expand All @@ -27,7 +69,29 @@ Feature: Check for new recipe versions
And the source of the sauce with recipe "foo" is in the local OCI registry
Then execution of the recipe has succeeded
And I check new versions for recipe "foo"
Then CLI produced an output "No new versions found"
Then CLI produced an output "no new versions found"

Scenario: Unable to find newer recipe versions for all recipes
Given a project directory
And a recipes directory
And a recipe "foo" that generates file "foo.md"
And a recipe "bar" that generates file "bar.md"
And a local OCI registry
When I execute recipe "foo"
And the source of the sauce with recipe "foo" is in the local OCI registry
Then execution of the recipe has succeeded
When I execute recipe "bar"
And the source of the sauce with recipe "bar" is in the local OCI registry
Then execution of the recipe has succeeded
When I push the recipe "foo" to the local OCI repository
When I push the recipe "bar" to the local OCI repository
Then no errors were printed
When I change recipe "foo" to version "v0.0.2"
And I push the recipe "foo" to the local OCI repository
Then no errors were printed
Then I check new versions
Then CLI produced an output "foo: new versions found: v0.0.2"
And CLI produced an output "bar: no new versions found"

Scenario: Executing remote recipe automatically adds the repo as source for the sauce
Given a project directory
Expand All @@ -41,4 +105,4 @@ Feature: Check for new recipe versions
Then execution of the recipe has succeeded
# Note the lack of explicitly setting the source for the sauce
And I check new versions for recipe "foo"
Then CLI produced an output "New versions found"
Then CLI produced an output "new versions found: v0.0.2"
68 changes: 29 additions & 39 deletions internal/cli/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package cli
import (
"context"
"fmt"
"strings"

"github.com/futurice/jalapeno/internal/cli/option"
"github.com/futurice/jalapeno/pkg/oci"
re "github.com/futurice/jalapeno/pkg/recipe"
"github.com/futurice/jalapeno/pkg/recipeutil"
"github.com/spf13/cobra"
"golang.org/x/mod/semver"
)

type checkOptions struct {
Expand All @@ -19,7 +19,11 @@ type checkOptions struct {
option.OCIRepository
}

const DetailedExitCodeWhenUpdatesAvailable = 2
const (
ExitCodeOK = 0
ExitCodeError = 0
ExitCodeUpdatesAvailable = 2
)

func NewCheckCmd() *cobra.Command {
var opts checkOptions
Expand All @@ -45,7 +49,7 @@ jalapeno check --recipe my-recipe`,
}

cmd.Flags().StringVar(&opts.RecipeName, "recipe", "", "Name of the recipe to check for new versions")
cmd.Flags().BoolVar(&opts.UseDetailedExitCode, "detailed-exitcode", false, fmt.Sprintf("Returns a detailed exit code when the command exits. When provided, this argument changes the exit codes and their meanings to provide more granular information about what the resulting plan contains: 0 = Succeeded with no updates available, 1 = Error, %d = Succeeded with updates available", DetailedExitCodeWhenUpdatesAvailable))
cmd.Flags().BoolVar(&opts.UseDetailedExitCode, "detailed-exitcode", false, fmt.Sprintf("Returns a detailed exit code when the command exits. When provided, this argument changes the exit codes and their meanings to provide more granular information about what the resulting plan contains: 0 = Succeeded with no updates available, 1 = Error, %d = Succeeded with updates available", ExitCodeUpdatesAvailable))

if err := option.ApplyFlags(&opts, cmd.Flags()); err != nil {
return nil
Expand Down Expand Up @@ -82,48 +86,34 @@ func runCheck(cmd *cobra.Command, opts checkOptions) error {

cmd.Println("Checking for new versions...")

updates := make([][]string, len(sauces))
for i, sauce := range sauces {
if sauce.CheckFrom == "" {
return fmt.Errorf("source of the sauce with ID '%s' is undefined, can not check for new versions", sauce.ID)
}

repo, err := oci.NewRepository(opts.Repository(sauce.CheckFrom))

updatesAvailable, errorsFound := false, false
for _, sauce := range sauces {
versions, err := recipeutil.CheckForUpdates(sauce, opts.OCIRepository)
if err != nil {
return err
}
errorsFound = true
cmd.Printf("%s: can not check for updates: %s\n", sauce.Recipe.Name, err)

ctx := context.Background()
err = repo.Tags(ctx, "", func(tags []string) error {
updates[i] = make([]string, 0, len(tags))
for _, tag := range tags {
if semver.IsValid(tag) && semver.Compare(tag, sauce.Recipe.Version) > 0 {
updates[i] = append(updates[i], tag)
}
}
semver.Sort(updates[i])
return nil
})
} else if len(versions) > 0 {
updatesAvailable = true
cmd.Printf("%s: new versions found: %s\n", sauce.Recipe.Name, strings.Join(versions, ", "))

if err != nil {
return err
} else {
cmd.Printf("%s: no new versions found\n", sauce.Recipe.Name)
}
}

for i, u := range updates {
if len(u) > 0 {
cmd.Printf("New versions found for recipe '%s': %s\n", sauces[i].Recipe.Name, u)
cmd.Println("Upgrade recipe with `jalapeno upgrade TODO`")

if opts.UseDetailedExitCode {
ctx := context.WithValue(cmd.Context(), ExitCodeContextKey{}, DetailedExitCodeWhenUpdatesAvailable)
cmd.SetContext(ctx)
}
} else {
cmd.Printf("No new versions found for recipe '%s'\n", sauces[i].Recipe.Name)
}
var exitCode int
switch {
case errorsFound:
exitCode = ExitCodeError
case updatesAvailable && opts.UseDetailedExitCode:
exitCode = ExitCodeUpdatesAvailable
default:
exitCode = ExitCodeOK
}

ctx := context.WithValue(cmd.Context(), ExitCodeContextKey{}, exitCode)
cmd.SetContext(ctx)

return nil
}
21 changes: 9 additions & 12 deletions internal/cli/check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ import (
re "github.com/futurice/jalapeno/pkg/recipe"
)

func iRunCheck(ctx context.Context, recipe string) (context.Context, error) {
func iRunCheck(ctx context.Context) (context.Context, error) {
projectDir := ctx.Value(projectDirectoryPathCtxKey{}).(string)
ociRegistry := ctx.Value(ociRegistryCtxKey{}).(OCIRegistry)
optionalFlags, flagsAreSet := ctx.Value(cmdOptionalFlagsCtxKey{}).(map[string]string)
additionalFlags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)

ctx, cmd := wrapCmdOutputs(ctx)

args := []string{
"check",
fmt.Sprintf("--recipe=%s", recipe),
fmt.Sprintf("--dir=%s", projectDir),
}

Expand All @@ -26,23 +25,21 @@ func iRunCheck(ctx context.Context, recipe string) (context.Context, error) {
args = append(args, "--plain-http=true")
}

if flagsAreSet && optionalFlags != nil {
for name, value := range optionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}
for name, value := range additionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}

cmd.SetArgs(args)
_ = cmd.Execute()
return ctx, nil
}

func newRecipeVersionsWereFound(ctx context.Context) (context.Context, error) {
return ctx, expectGivenOutput(ctx, "New versions found")
}
func iRunCheckForRecipe(ctx context.Context, recipe string) (context.Context, error) {
additionalFlags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)
additionalFlags["recipe"] = recipe

func noNewRecipeVersionsWereFound(ctx context.Context) (context.Context, error) {
return ctx, expectGivenOutput(ctx, "No new versions found")
ctx = context.WithValue(ctx, cmdAdditionalFlagsCtxKey{}, additionalFlags)
return iRunCheck(ctx)
}

func sourceOfTheSauceIsTheLocalOCIRegistry(ctx context.Context, recipeName string) (context.Context, error) {
Expand Down
14 changes: 10 additions & 4 deletions internal/cli/cmds_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type (
ociRegistryCtxKey struct{}
cmdStdOutCtxKey struct{}
cmdStdErrCtxKey struct{}
cmdOptionalFlagsCtxKey struct{}
cmdAdditionalFlagsCtxKey struct{}
dockerResourcesCtxKey struct{}
)

Expand Down Expand Up @@ -80,9 +80,8 @@ func TestFeatures(t *testing.T) {
s.Step(`^CLI produced an output "([^"]*)"$`, expectGivenOutput)
s.Step(`^CLI produced an error "([^"]*)"$`, expectGivenError)
s.Step(`^I change recipe "([^"]*)" to version "([^"]*)"$`, iChangeRecipeToVersion)
s.Step(`^I check new versions for recipe "([^"]*)"$`, iRunCheck)
s.Step(`^newer recipe versions were found`, newRecipeVersionsWereFound)
s.Step(`^no newer recipe versions were found`, noNewRecipeVersionsWereFound)
s.Step(`^I check new versions$`, iRunCheck)
s.Step(`^I check new versions for recipe "([^"]*)"$`, iRunCheckForRecipe)
s.Step(`^I upgrade recipe "([^"]*)"$`, iRunUpgrade)
s.Step(`^I upgrade recipe from the local OCI repository "([^"]*)"$`, iRunUpgradeFromRemoteRecipe)
s.Step(`^I create a recipe with name "([^"]*)"$`, iRunCreate)
Expand All @@ -105,6 +104,13 @@ func TestFeatures(t *testing.T) {
s.Step(`^there should not be a sauce directory in the project directory$`, thereShouldNotBeASauceDirectoryInTheProjectDirectory)
s.Step(`I check why the file "([^"]*)" is created$`, iRunWhy)
s.Step(`I validate recipe "([^"]*)"$`, iRunValidate)
s.Before(func(ctx context.Context, sc *godog.Scenario) (context.Context, error) {
// Initialize additional flags to empty map before each step
return context.WithValue(
ctx,
cmdAdditionalFlagsCtxKey{},
make(map[string]string)), nil
})
s.After(cleanDockerResources)
s.After(cleanTempDirs)
},
Expand Down
18 changes: 5 additions & 13 deletions internal/cli/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import (

func iRunExecute(ctx context.Context, recipe string) (context.Context, error) {
projectDir := ctx.Value(projectDirectoryPathCtxKey{}).(string)
optionalFlags, flagsAreSet := ctx.Value(cmdOptionalFlagsCtxKey{}).(map[string]string)
additionalFlags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)

ctx, cmd := wrapCmdOutputs(ctx)

Expand All @@ -28,10 +28,8 @@ func iRunExecute(ctx context.Context, recipe string) (context.Context, error) {
fmt.Sprintf("--dir=%s", projectDir),
}

if flagsAreSet && optionalFlags != nil {
for name, value := range optionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}
for name, value := range additionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}

cmd.SetArgs(args)
Expand All @@ -42,13 +40,7 @@ func iRunExecute(ctx context.Context, recipe string) (context.Context, error) {
func iExecuteRemoteRecipe(ctx context.Context, repository string) (context.Context, error) {
ociRegistry := ctx.Value(ociRegistryCtxKey{}).(OCIRegistry)
configDir, configFileExists := ctx.Value(dockerConfigDirectoryPathCtxKey{}).(string)
optionalFlags, flagsAreSet := ctx.Value(cmdOptionalFlagsCtxKey{}).(map[string]string)
var flags map[string]string
if flagsAreSet {
flags = optionalFlags
} else {
flags = make(map[string]string)
}
flags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)

url := fmt.Sprintf("oci://%s/%s", ociRegistry.Resource.GetHostPort("5000/tcp"), repository)

Expand All @@ -68,7 +60,7 @@ func iExecuteRemoteRecipe(ctx context.Context, repository string) (context.Conte
flags["registry-config"] = filepath.Join(configDir, DOCKER_CONFIG_FILENAME)
}

ctx = context.WithValue(ctx, cmdOptionalFlagsCtxKey{}, flags)
ctx = context.WithValue(ctx, cmdAdditionalFlagsCtxKey{}, flags)

return iRunExecute(ctx, url)
}
Expand Down
8 changes: 3 additions & 5 deletions internal/cli/pull_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func iPullRecipe(ctx context.Context, recipeName, repoName string) (context.Cont
recipesDir := ctx.Value(recipesDirectoryPathCtxKey{}).(string)
ociRegistry := ctx.Value(ociRegistryCtxKey{}).(OCIRegistry)
configDir, configFileExists := ctx.Value(dockerConfigDirectoryPathCtxKey{}).(string)
optionalFlags, flagsAreSet := ctx.Value(cmdOptionalFlagsCtxKey{}).(map[string]string)
additionalFlags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)

ctx, cmd := wrapCmdOutputs(ctx)

Expand Down Expand Up @@ -39,10 +39,8 @@ func iPullRecipe(ctx context.Context, recipeName, repoName string) (context.Cont
)
}

if flagsAreSet && optionalFlags != nil {
for name, value := range optionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}
for name, value := range additionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}

cmd.SetArgs(args)
Expand Down
8 changes: 3 additions & 5 deletions internal/cli/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ func iRunPush(ctx context.Context, recipeName string) (context.Context, error) {
recipesDir := ctx.Value(recipesDirectoryPathCtxKey{}).(string)
ociRegistry := ctx.Value(ociRegistryCtxKey{}).(OCIRegistry)
configDir, configFileExists := ctx.Value(dockerConfigDirectoryPathCtxKey{}).(string)
optionalFlags, flagsAreSet := ctx.Value(cmdOptionalFlagsCtxKey{}).(map[string]string)
additionalFlags := ctx.Value(cmdAdditionalFlagsCtxKey{}).(map[string]string)

ctx, cmd := wrapCmdOutputs(ctx)

Expand Down Expand Up @@ -39,10 +39,8 @@ func iRunPush(ctx context.Context, recipeName string) (context.Context, error) {
)
}

if flagsAreSet && optionalFlags != nil {
for name, value := range optionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}
for name, value := range additionalFlags {
args = append(args, fmt.Sprintf("--%s=%s", name, value))
}

cmd.SetArgs(args)
Expand Down
Loading

0 comments on commit 3d715f7

Please sign in to comment.