-
Notifications
You must be signed in to change notification settings - Fork 103
/
shell.go
126 lines (114 loc) · 3.26 KB
/
shell.go
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package launch
import (
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/pkg/errors"
"github.com/buildpacks/lifecycle/api"
)
type Shell interface {
Launch(ShellProcess) error
}
type ShellProcess struct {
Script bool // Script indicates whether Command is a script or should be a token in a generated script
Args []string
Command string
Caller string // Caller used to set argv0 for Bash profile scripts and is ignored in Cmd
Profiles []string
Env []string
WorkingDirectory string
}
func (l *Launcher) launchWithShell(self string, proc Process) error {
profs, err := l.getProfiles(proc.Type)
if err != nil {
return errors.Wrap(err, "find profiles")
}
script, err := l.isScript(proc)
if err != nil {
return err
}
command := ""
if len(proc.Command.Entries) > 0 {
command = strings.Join(proc.Command.Entries, " ")
}
return l.Shell.Launch(ShellProcess{
Script: script,
Caller: self,
Command: command,
Args: proc.Args,
Profiles: profs,
Env: l.Env.List(),
WorkingDirectory: proc.WorkingDirectory,
})
}
func (l *Launcher) getProfiles(procType string) ([]string, error) {
var profiles []string
if err := l.eachBuildpack(func(_ *api.Version, bpDir string) error {
return eachLayer(bpDir, l.populateLayerProfiles(procType, &profiles))
}); err != nil {
return nil, err
}
fi, err := os.Stat(filepath.Join(l.AppDir, appProfile))
if os.IsNotExist(err) {
return profiles, nil
} else if err != nil {
return nil, errors.Wrapf(err, "failed to determine if app profile script exists at path '%s'", filepath.Join(l.AppDir, appProfile))
}
if !fi.IsDir() {
profiles = append(profiles, appProfile)
}
return profiles, nil
}
func (l *Launcher) populateLayerProfiles(procType string, profiles *[]string) dirAction {
return func(layerDir string) error {
if err := eachFile(filepath.Join(layerDir, "profile.d"), func(path string) error {
*profiles = append(*profiles, path)
return nil
}); err != nil {
return err
}
if procType == "" {
return nil
}
return eachFile(filepath.Join(layerDir, "profile.d", procType), func(path string) error {
*profiles = append(*profiles, path)
return nil
})
}
}
func (l *Launcher) isScript(proc Process) (bool, error) {
if runtime.GOOS == "windows" {
// Windows does not support script commands
return false, nil
}
if len(proc.Args) == 0 {
return true, nil
}
bpAPI, err := l.buildpackAPI(proc)
if err != nil {
return false, err
}
if bpAPI == nil {
return false, err
}
return false, nil
}
// buildpackAPI returns the API of the buildpack that contributed the process and true if the process was contributed
// by a buildpack. If the process was not provided by a buildpack it returns nil.
func (l *Launcher) buildpackAPI(proc Process) (*api.Version, error) {
if proc.BuildpackID == "" {
return nil, nil
}
for _, bp := range l.Buildpacks {
if bp.ID == proc.BuildpackID {
api, err := api.NewVersion(bp.API)
if err != nil {
return nil, errors.Wrapf(err, "failed to parse api '%s' of buildpack '%s'", bp.API, bp.ID)
}
return api, nil
}
}
return nil, fmt.Errorf("process type '%s' provided by unknown buildpack '%s'", proc.Type, proc.BuildpackID)
}