Skip to content

Commit b363381

Browse files
committed
feat: implement commands.*.after
1 parent 83a90f7 commit b363381

8 files changed

Lines changed: 128 additions & 13 deletions

File tree

cmd/overwrite_command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,5 @@ func NewOverwriteCmd(f factory.Factory, globalFlags *flags.GlobalFlags, command
6666

6767
func (cmd *OverwriteCmd) Run(f factory.Factory, args []string) error {
6868
devCtx := devspacecontext.NewContext(context.Background(), f.GetLog())
69-
return ExecuteCommand(devCtx.Context, cmd.Command, cmd.Variables, args, devCtx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin)
69+
return executeCommandWithAfter(devCtx.Context, cmd.Command, args, cmd.Variables, devCtx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin, devCtx.Log)
7070
}

cmd/run.go

Lines changed: 67 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/loft-sh/devspace/pkg/devspace/plugin"
1212
"github.com/loft-sh/devspace/pkg/util/command"
1313
"github.com/loft-sh/devspace/pkg/util/exit"
14+
"github.com/loft-sh/devspace/pkg/util/interrupt"
1415
"github.com/loft-sh/devspace/pkg/util/log"
1516
"io"
1617
"mvdan.cc/sh/v3/interp"
@@ -158,11 +159,57 @@ func (cmd *RunCmd) RunRun(f factory.Factory, args []string) error {
158159
}
159160

160161
ctx = ctx.AsDependency(dep)
161-
return ExecuteConfigCommand(ctx.Context, ctx.Config, args[0], args[1:], ctx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin)
162+
commandConfig, err := findCommand(ctx.Config, args[0])
163+
if err != nil {
164+
return err
165+
}
166+
167+
return executeCommandWithAfter(ctx.Context, commandConfig, args[1:], ctx.Config.Variables(), ctx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin, ctx.Log)
162168
}
163169

164-
// Execute command
165-
return ExecuteConfigCommand(ctx.Context, ctx.Config, args[0], args[1:], ctx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin)
170+
commandConfig, err := findCommand(ctx.Config, args[0])
171+
if err != nil {
172+
return err
173+
}
174+
175+
return executeCommandWithAfter(ctx.Context, commandConfig, args[1:], ctx.Config.Variables(), ctx.WorkingDir, cmd.Stdout, cmd.Stderr, os.Stdin, ctx.Log)
176+
}
177+
178+
func findCommand(config config.Config, name string) (*latest.CommandConfig, error) {
179+
// Find command
180+
if config.Config().Commands == nil || config.Config().Commands[name] == nil {
181+
return nil, errors.Errorf("couldn't find command '%s' in devspace config", name)
182+
}
183+
184+
return config.Config().Commands[name], nil
185+
}
186+
187+
func executeCommandWithAfter(ctx context.Context, command *latest.CommandConfig, args []string, variables map[string]interface{}, dir string, stdout io.Writer, stderr io.Writer, stdin io.Reader, log log.Logger) (err error) {
188+
err = interrupt.Global.Run(func() error {
189+
return ExecuteCommand(ctx, command, variables, args, dir, stdout, stderr, stdin)
190+
}, func() {
191+
if command.After != "" {
192+
vars := variables
193+
vars["COMMAND_INTERRUPT"] = "true"
194+
err = executeShellCommand(ctx, command.After, vars, args, dir, stdout, stderr, stdin)
195+
if err != nil {
196+
log.Errorf("error executing after command: %v", err)
197+
}
198+
}
199+
})
200+
if command.After != "" {
201+
vars := variables
202+
if err != nil {
203+
vars["COMMAND_ERROR"] = err.Error()
204+
log.Error(err)
205+
}
206+
err = executeShellCommand(ctx, command.After, vars, args, dir, stdout, stderr, stdin)
207+
if err != nil {
208+
return errors.Wrap(err, "error executing after command")
209+
}
210+
return nil
211+
}
212+
return err
166213
}
167214

168215
func ParseArgs(cobraCmd *cobra.Command, globalFlags *flags.GlobalFlags, log log.Logger) ([]string, error) {
@@ -221,14 +268,25 @@ func LoadCommandsConfig(configLoader loader.ConfigLoader, configOptions *loader.
221268
WithConfig(commandsInterface), nil
222269
}
223270

224-
// ExecuteConfigCommand executes a command from the config
225-
func ExecuteConfigCommand(ctx context.Context, config config.Config, name string, args []string, dir string, stdout io.Writer, stderr io.Writer, stdin io.Reader) error {
226-
if config.Config().Commands == nil || config.Config().Commands[name] == nil {
227-
return errors.Errorf("couldn't find command '%s' in devspace config", name)
271+
func executeShellCommand(ctx context.Context, shellCommand string, variables map[string]interface{}, args []string, dir string, stdout io.Writer, stderr io.Writer, stdin io.Reader) error {
272+
extraEnv := map[string]string{}
273+
for k, v := range variables {
274+
extraEnv[k] = fmt.Sprintf("%v", v)
275+
}
276+
277+
// execute the command in a shell
278+
err := engine.ExecuteSimpleShellCommand(ctx, dir, stdout, stderr, stdin, extraEnv, shellCommand, args...)
279+
if err != nil {
280+
if status, ok := interp.IsExitStatus(err); ok {
281+
return &exit.ReturnCodeError{
282+
ExitCode: int(status),
283+
}
284+
}
285+
286+
return errors.Wrap(err, "execute command")
228287
}
229288

230-
cmd := config.Config().Commands[name]
231-
return ExecuteCommand(ctx, cmd, config.Variables(), args, dir, stdout, stderr, stdin)
289+
return nil
232290
}
233291

234292
// ExecuteCommand executes a command from the config

pkg/devspace/config/config.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,12 @@ func (c *config) LocalCache() localcache.Cache {
8080
}
8181

8282
func (c *config) Variables() map[string]interface{} {
83-
return c.resolvedVariables
83+
newVariables := map[string]interface{}{}
84+
for k, v := range c.resolvedVariables {
85+
newVariables[k] = v
86+
}
87+
88+
return newVariables
8489
}
8590

8691
func (c *config) Path() string {

pkg/devspace/config/versions/latest/schema.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,6 +1324,12 @@ type CommandConfig struct {
13241324
// Command is the command that should be executed. For example: 'echo 123'
13251325
Command string `yaml:"command" json:"command"`
13261326

1327+
// After is executed after the command was run. It is executed also when
1328+
// the command was interrupted which will set the env variable COMMAND_INTERRUPT
1329+
// to true as well as when the command errored which will set the error string to
1330+
// COMMAND_ERROR.
1331+
After string `yaml:"after" json:"after"`
1332+
13271333
// DisableReplace signals DevSpace to not replace the default command. E.g.
13281334
// dev does not replace devspace dev.
13291335
DisableReplace bool `yaml:"disableReplace,omitempty" json:"disableReplace,omitempty"`
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package commands
2+
3+
import (
4+
"mvdan.cc/sh/v3/interp"
5+
"strings"
6+
)
7+
8+
func IsEmpty(args []string) error {
9+
// its possible that there are 0 args
10+
// because bash will omit an empty string
11+
// as argument
12+
if len(args) > 1 {
13+
return interp.NewExitStatus(1)
14+
} else if len(args) == 0 {
15+
return interp.NewExitStatus(0)
16+
}
17+
18+
if strings.TrimSpace(args[0]) == "" {
19+
return interp.NewExitStatus(0)
20+
}
21+
return interp.NewExitStatus(1)
22+
}

pkg/devspace/pipeline/engine/basichandler/commands/is_equal.go

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,29 @@ package commands
22

33
import (
44
"mvdan.cc/sh/v3/interp"
5+
"strings"
56
)
67

78
func IsEqual(args []string) error {
8-
if len(args) != 2 {
9+
if len(args) > 2 {
910
return interp.NewExitStatus(1)
1011
}
1112

13+
// one of the arguments is an empty string
14+
if len(args) == 1 {
15+
if strings.TrimSpace(args[0]) == "" {
16+
return interp.NewExitStatus(0)
17+
}
18+
19+
return interp.NewExitStatus(1)
20+
}
21+
22+
// both arguments are empty strings
23+
if len(args) == 0 {
24+
return interp.NewExitStatus(0)
25+
}
26+
27+
// compare arguments
1228
if args[0] == args[1] {
1329
return interp.NewExitStatus(0)
1430
}

pkg/devspace/pipeline/engine/basichandler/commands/is_os.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import (
66
)
77

88
func IsOS(args []string) error {
9-
if len(args) != 1 {
9+
if len(args) > 1 {
10+
return interp.NewExitStatus(1)
11+
}
12+
13+
// is empty string?
14+
if len(args) == 0 {
1015
return interp.NewExitStatus(1)
1116
}
1217

pkg/devspace/pipeline/engine/basichandler/handler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ var BasicCommands = map[string]func(ctx context.Context, args []string) error{
2323
"is_equal": func(ctx context.Context, args []string) error {
2424
return enginecommands.IsEqual(args)
2525
},
26+
"is_empty": func(ctx context.Context, args []string) error {
27+
return enginecommands.IsEmpty(args)
28+
},
2629
"is_true": func(ctx context.Context, args []string) error {
2730
return enginecommands.IsTrue(args)
2831
},

0 commit comments

Comments
 (0)