-
Notifications
You must be signed in to change notification settings - Fork 50
/
exec_cmd.go
112 lines (97 loc) · 2.99 KB
/
exec_cmd.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
package app
import (
"net/http"
"os"
"path/filepath"
"strings"
"syscall"
"time"
"github.com/pkg/errors"
"github.com/cashapp/hermit"
"github.com/cashapp/hermit/manifest"
"github.com/cashapp/hermit/state"
"github.com/cashapp/hermit/ui"
"github.com/cashapp/hermit/util/debug"
)
type execCmd struct {
Binary string `arg:"" help:"Binary symlink to execute."`
Args []string `arg:"" help:"Arguments to pass to executable (use -- to separate)." optional:""`
}
func (e *execCmd) Run(l *ui.UI, sta *state.State, env *hermit.Env, globalState GlobalState, config Config, defaultHTTPClient *http.Client) error {
envDir, err := hermit.EnvDirFromProxyLink(e.Binary)
if err != nil {
return errors.WithStack(err)
}
if env == nil {
env, err = hermit.OpenEnv(envDir, sta, globalState.Env, defaultHTTPClient)
if err != nil {
return errors.WithStack(err)
}
}
args := []string{e.Binary}
args = append(args, e.Args...)
self, err := os.Executable()
if err != nil {
return errors.WithStack(err)
}
// Upgrade hermit if necessary
pkgRef := filepath.Base(filepath.Dir(self))
if strings.HasPrefix(pkgRef, "hermit@") {
err := updateHermit(l, env, pkgRef, false)
if err != nil {
return errors.WithStack(err)
}
}
// Special-case executing Hermit itself.
if filepath.Base(e.Binary) == "hermit" {
env := os.Environ()
env = append(env, "HERMIT_ENV="+envDir)
return syscall.Exec(self, args, env)
}
// Check that if we are running from an activated environment, it is the correct one
activeEnv := os.Getenv("ACTIVE_HERMIT")
if activeEnv != "" && envDir != "" && activeEnv != envDir {
return errors.New("can not execute a Hermit managed binary from a non active environment")
}
pkg, binary, err := env.ResolveLink(l, e.Binary)
if err != nil {
return errors.WithStack(err)
}
if err := pkg.EnsureSupported(); err != nil {
return errors.Wrapf(err, "execution failed")
}
installed, err := env.ListInstalled(l)
if err != nil {
return errors.WithStack(err)
}
// Collect dependencies we might have to install
// if they are not in the cache
deps := map[string]*manifest.Package{}
err = env.ResolveWithDeps(l, installed, manifest.ExactSelector(pkg.Reference), deps)
if err != nil {
return errors.WithStack(err)
}
return env.Exec(l, pkg, binary, args, deps)
}
func updateHermit(l *ui.UI, env *hermit.Env, pkgRef string, force bool) error {
l.Tracef("Checking if %s needs to be updated", pkgRef)
pkg, err := env.Resolve(l, manifest.ExactSelector(manifest.ParseReference(pkgRef)), false)
if err != nil {
return errors.WithStack(err)
}
// Mark Hermit updated if this is a new installation to prevent immediate upgrade checks
if force {
pkg.UpdatedAt = time.Time{}
} else if pkg.UpdatedAt.IsZero() {
pkg.UpdatedAt = time.Now()
}
err = env.UpdateUsage(pkg)
if err != nil {
return errors.WithStack(err)
}
if debug.Flags.AlwaysCheckSelf {
// set the update time to 0 to force an update check
pkg.UpdatedAt = time.Time{}
}
return errors.WithStack(env.EnsureChannelIsUpToDate(l, pkg))
}