-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexec.go
More file actions
81 lines (73 loc) · 2.33 KB
/
exec.go
File metadata and controls
81 lines (73 loc) · 2.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
package devcontainer
import (
"context"
"fmt"
"io"
"github.com/crunchloop/devcontainer/runtime"
)
// ExecOptions configures Engine.Exec. Cmd, Env, User, and WorkingDir all
// pass through Workspace.Substituter so ${containerEnv:*} placeholders
// resolve against the live container.
type ExecOptions struct {
Cmd []string
Env map[string]string
User string
WorkingDir string
Tty bool
Stdin io.Reader
Stdout io.Writer
Stderr io.Writer
}
// ExecResult is the outcome of Engine.Exec.
type ExecResult struct {
ExitCode int
Stdout string // populated only if ExecOptions.Stdout was nil
Stderr string // populated only if ExecOptions.Stderr was nil
}
// Exec runs a command inside the workspace's container. Strings in
// opts.Cmd, opts.Env values, opts.User, and opts.WorkingDir are
// substituted against the live container's environment before being
// handed to the runtime. ${containerEnv:VAR} resolves to the container's
// actual value; missing entries substitute to empty string with a
// (discarded) warning, matching VS Code semantics.
func (e *Engine) Exec(ctx context.Context, ws *Workspace, opts ExecOptions) (ExecResult, error) {
if err := ctxIfDone(ctx); err != nil {
return ExecResult{}, err
}
if ws == nil {
return ExecResult{}, fmt.Errorf("Engine.Exec: Workspace is required")
}
cmd, _ := ws.subst.Slice(opts.Cmd)
env, _ := ws.subst.Map(opts.Env)
user, _ := ws.subst.String(opts.User)
wd, _ := ws.subst.String(opts.WorkingDir)
res, err := e.runtime.ExecContainer(ctx, ws.Container.ID, runtime.ExecOptions{
Cmd: cmd,
Env: env,
User: user,
WorkingDir: wd,
Tty: opts.Tty,
Stdin: opts.Stdin,
Stdout: opts.Stdout,
Stderr: opts.Stderr,
})
if err != nil {
return ExecResult{}, err
}
return ExecResult{
ExitCode: res.ExitCode,
Stdout: res.Stdout,
Stderr: res.Stderr,
}, nil
}
// ExecByID is a convenience wrapper around Attach + Exec for callers that
// hold only a WorkspaceID. Hot-loop callers that hold a *Workspace from
// Up should call Exec directly to avoid re-inspecting the container on
// every invocation.
func (e *Engine) ExecByID(ctx context.Context, id WorkspaceID, opts ExecOptions) (ExecResult, error) {
ws, err := e.Attach(ctx, id)
if err != nil {
return ExecResult{}, err
}
return e.Exec(ctx, ws, opts)
}