Skip to content

Commit

Permalink
fix: skip already executed recipes when executing a manifest
Browse files Browse the repository at this point in the history
  • Loading branch information
majori committed Apr 17, 2024
1 parent 076d844 commit 9463a4f
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 8 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ bin/
.task

temp
.env
.env

.docusaurus
24 changes: 19 additions & 5 deletions internal/cli/execute.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,17 @@ type executeOptions struct {
option.Timeout
}

var (
ErrAlreadyExecuted = errors.New("recipe has been already executed")
)

func NewExecuteCmd() *cobra.Command {
var opts executeOptions
var cmd = &cobra.Command{
Use: "execute RECIPE_URL",
Aliases: []string{"exec", "e", "run"},
Short: "Execute a recipe",
Long: "Executes (renders) a recipe and outputs the files to the directory. Recipe URL can be a local path or a remote URL (ex. 'oci://docker.io/my-recipe').",
Short: "Execute a recipe or manifest",
Long: "Executes (renders) a recipe or manifest and outputs the files to the directory. Recipe URL can be a local path or a remote URL (ex. 'oci://docker.io/my-recipe').",
Args: cobra.ExactArgs(1),
PreRunE: func(cmd *cobra.Command, args []string) error {
opts.RecipeURL = args[0]
Expand All @@ -59,6 +63,9 @@ jalapeno execute oci://ghcr.io/user/my-recipe:latest --username user --password
docker login ghcr.io
jalapeno execute oci://ghcr.io/user/my-recipe:latest
# Execute a manifest which contains multiple recipes
jalapeno execute path/to/manifest.yml
# Execute recipe to different directory
jalapeno execute path/to/recipe --dir other/dir
Expand Down Expand Up @@ -128,6 +135,7 @@ func runExecute(cmd *cobra.Command, opts executeOptions) error {

func executeRecipe(cmd *cobra.Command, opts executeOptions, re *recipe.Recipe) error {
cmd.Printf("%s: %s\n", opts.Colors.Red.Render("Recipe name"), re.Metadata.Name)
cmd.Printf("%s: %s\n", opts.Colors.Red.Render("Version"), re.Metadata.Version)

if re.Metadata.Description != "" {
cmd.Printf("%s: %s\n", opts.Colors.Red.Render("Description"), re.Metadata.Description)
Expand All @@ -145,7 +153,7 @@ func executeRecipe(cmd *cobra.Command, opts executeOptions, re *recipe.Recipe) e
if sauce.Recipe.Name == re.Name &&
semver.Compare(sauce.Recipe.Metadata.Version, re.Metadata.Version) == 0 &&
sauce.SubPath == opts.Subpath {
return fmt.Errorf("recipe '%s' with version '%s' has been already executed. If you want to re-execute the recipe with different values, use `upgrade` command with `--reuse-old-values=false` flag", re.Name, re.Metadata.Version)
return fmt.Errorf("recipe '%s@%s': %w. If you want to re-execute the recipe with different values, use `upgrade` command with `--reuse-old-values=false` flag", re.Name, re.Metadata.Version, ErrAlreadyExecuted)
}
}

Expand Down Expand Up @@ -268,7 +276,9 @@ func executeRecipe(cmd *cobra.Command, opts executeOptions, re *recipe.Recipe) e
}

func executeManifest(cmd *cobra.Command, opts executeOptions, manifest *recipe.Manifest) error {
cmd.Printf("Executing manifest with %d recipes...\n\n", len(manifest.Recipes))
if len(manifest.Recipes) > 1 {
cmd.Printf("Executing manifest with %d recipes...\n\n", len(manifest.Recipes))
}

if len(opts.Values.Flags) > 0 {
return errors.New("values can not be provided when executing a manifest. Use values in the manifest file instead")
Expand Down Expand Up @@ -305,7 +315,11 @@ func executeManifest(cmd *cobra.Command, opts executeOptions, manifest *recipe.M

err = executeRecipe(cmd, opts, re)
if err != nil {
return err
if errors.Is(err, ErrAlreadyExecuted) {
cmd.Printf("Recipe '%s@%s' has already been executed, skipping...\n", re.Name, re.Version)
} else {
return err
}
}

if i < len(manifest.Recipes)-1 {
Expand Down
4 changes: 4 additions & 0 deletions pkg/recipe/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ func (m *Manifest) Validate() error {
return fmt.Errorf("unreconized manifest API version \"%s\"", m.APIVersion)
}

if len(m.Recipes) == 0 {
return fmt.Errorf("manifest must contain at least one recipe")
}

for _, r := range m.Recipes {
if r.Name == "" {
return fmt.Errorf("recipe name is required")
Expand Down
19 changes: 19 additions & 0 deletions test/execute_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ func AddExecuteSteps(s *godog.ScenarioContext) {
s.Step(`^I execute the recipe from the local OCI repository "([^"]*)"$`, iExecuteRemoteRecipe)
s.Step(`^recipes will be executed to the subpath "([^"]*)"$`, recipesWillBeExecutedToTheSubPath)
s.Step(`^execution of the recipe has succeeded$`, executionOfTheRecipeHasSucceeded)
s.Step(`^a manifest file$`, aManifestFile)
s.Step(`^a manifest file that includes recipes$`, aManifestFileThatIncludesRecipes)
s.Step(`^a manifest file that includes remote recipes$`, aManifestFileThatIncludesRemoteRecipes)
s.Step(`^I execute the manifest file$`, iExecuteTheManifestFile)
Expand Down Expand Up @@ -72,6 +73,24 @@ func executionOfTheRecipeHasSucceeded(ctx context.Context) (context.Context, err
return ctx, expectGivenOutput(ctx, "Recipe executed successfully")
}

func aManifestFile(ctx context.Context) (context.Context, error) {
dir, err := os.MkdirTemp("", "jalapeno-test-manifest")
if err != nil {
return ctx, err
}

manifest := recipe.NewManifest()

err = manifest.Save(filepath.Join(dir, TestManifestFileName))
if err != nil {
return ctx, err
}

ctx = context.WithValue(ctx, manifestDirectoryPathCtxKey{}, dir)

return ctx, nil
}

func aManifestFileThatIncludesRecipes(ctx context.Context, recipeNames *godog.Table) (context.Context, error) {
recipeDir := ctx.Value(recipesDirectoryPathCtxKey{}).(string)
dir, err := os.MkdirTemp("", "jalapeno-test-manifest")
Expand Down
29 changes: 29 additions & 0 deletions test/features/execute-manifest.feature
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ Feature: Execute manifests
And CLI produced an output "Recipe name: bar"
And the project directory should contain file "foo.md"
And the project directory should contain file "bar.md"

Scenario: Execute a manifest with no recipes
Given a project directory
And a recipes directory
And a manifest file
When I execute the manifest file
Then CLI produced an error "^Error: can not load the manifest: manifest must contain at least one recipe"

Scenario: Conflicting recipes in manifest results in an error
Given a project directory
Expand All @@ -52,3 +59,25 @@ Feature: Execute manifests
| conflicts-with-foo |
When I execute the manifest file
Then CLI produced an error "^Error: conflict in recipe 'conflicts-with-foo': file 'foo\.md' was already created by recipe 'foo'\."

Scenario: Already executed recipes are skipped when executing a manifest
Given a project directory
And a recipes directory
And a recipe "foo"
And recipe "foo" generates file "foo.md" with content "initial"
And a recipe "bar"
And recipe "bar" generates file "bar.md" with content "initial"
And a manifest file that includes recipes
| foo |
When I execute the manifest file
Then no errors were printed

Given I clear the output
And a manifest file that includes recipes
| foo |
| bar |
When I execute the manifest file
Then no errors were printed
And CLI produced an output "Recipe '[email protected]' has already been executed, skipping"
And the project directory should contain file "foo.md"
And the project directory should contain file "bar.md"
2 changes: 1 addition & 1 deletion test/features/execute-recipes.feature
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Feature: Execute recipes
And no errors were printed
Then execution of the recipe has succeeded
When I execute recipe "foo"
Then CLI produced an error "recipe 'foo' with version 'v0.0.1' has been already executed"
Then CLI produced an error "recipe 'foo@v0\.0\.1': recipe has been already executed"

Scenario: Execute single recipe to a subpath
Given a project directory
Expand Down
4 changes: 3 additions & 1 deletion test/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,9 @@ func theProjectDirectoryShouldNotContainFile(ctx context.Context, filename strin
}

func iClearTheOutput(ctx context.Context) (context.Context, error) {
return context.WithValue(ctx, cmdStdOutCtxKey{}, new(bytes.Buffer)), nil
ctx = context.WithValue(ctx, cmdStdOutCtxKey{}, new(bytes.Buffer))
ctx = context.WithValue(ctx, cmdStdErrCtxKey{}, new(bytes.Buffer))
return ctx, nil
}

func theProjectDirectoryShouldContainFile(ctx context.Context, filename string) error {
Expand Down

0 comments on commit 9463a4f

Please sign in to comment.