diff --git a/dev/sg/internal/run/run.go b/dev/sg/internal/run/run.go index 7444887f3a5..aa2c3e337b1 100644 --- a/dev/sg/internal/run/run.go +++ b/dev/sg/internal/run/run.go @@ -13,6 +13,8 @@ import ( "sync" "time" + "bitbucket.org/creachadair/shell" + "github.com/bitfield/script" "github.com/grafana/regexp" "github.com/rjeczalik/notify" @@ -77,7 +79,10 @@ func Commands(ctx context.Context, globalEnv map[string]string, addToMacOSFirewa }(cmd, chs[i]) } - postInstall := newPostInstall(ctx, cmds, addToMacOSFirewall) + postInstall := func() error { return nil } + if addToMacOSFirewall || runtime.GOOS == "darwin" { + postInstall = addToMacosFirewall(cmds) + } err = waitForInstallation(cmdNames, installed, failures, okayToStart, postInstall) if err != nil { return err @@ -94,38 +99,70 @@ func Commands(ctx context.Context, globalEnv map[string]string, addToMacOSFirewa } } -func newPostInstall(ctx context.Context, cmds []Command, addToMacOSFirewall bool) func() error { - if !addToMacOSFirewall || runtime.GOOS != "darwin" { - return func() error { return nil } - } - +// addToMacosFirewall returns a callback that is used to add binaries used by the given +// commands to the MacOS firewall. +func addToMacosFirewall(cmds []Command) func() error { return func() error { root, err := root.RepositoryRoot() if err != nil { return err } - fwCmdPath := "/usr/libexec/ApplicationFirewall/socketfilterfw" stdout.Out.WriteLine(output.Linef(output.EmojiWarningSign, output.StyleWarning, "You may be prompted to enter your password to add exceptions to the firewall.")) - fcmd := exec.CommandContext(ctx, "sudo", fwCmdPath, "--setglobalstate", "off") - err = fcmd.Run() - if err != nil { - return err - } + + // http://www.manpagez.com/man/8/socketfilterfw/ + firewallCmdPath := "/usr/libexec/ApplicationFirewall/socketfilterfw" + var needsFirewallRestart bool + + // Add binaries in '.bin' to firewall for _, cmd := range cmds { - if strings.HasPrefix(cmd.Cmd, ".bin/") { - fcmd = exec.CommandContext(ctx, "sudo", fwCmdPath, "--add", filepath.Join(root, cmd.Cmd)) - err = fcmd.Run() + // Some commands use env variables that may be from command env or global env, + // so do substitutions and get the binary we want to work with. + args, ok := shell.Split(os.Expand(cmd.Cmd, func(key string) string { + if v, exists := cmd.Env[key]; exists { + return v + } + return os.Getenv(key) + })) + if !ok || len(args) == 0 { + stdout.Out.WriteLine(output.Linef(output.EmojiFailure, output.StyleSuggestion, "%s: invalid command", cmd.Cmd)) + continue + } + + binary := args[0] + if strings.HasPrefix(binary, ".bin/") || strings.HasPrefix(binary, "./.bin/") { + addException := script.Exec(shell.Join([]string{"sudo", firewallCmdPath, "--add", filepath.Join(root, binary)})) + msg, err := addException.String() if err != nil { - return err + stdout.Out.WriteLine(output.Linef(output.EmojiFailure, output.StyleBold, "%s: %s", binary, err.Error())) + continue + } + + // socketfilterfw helpfully always returns status 0, so we need to check + // the output to determine whether things worked or not. In all cases we + // don't error out becasue we want other commands to go through the firewall + // updates regardless. + switch { + case strings.Contains(msg, "does not exist"): + stdout.Out.WriteLine(output.Linef(output.EmojiFailure, output.StyleWarning, "%s: %s", binary, strings.TrimSpace(msg))) + + case strings.Contains(msg, "added to firewall"): + stdout.Out.WriteLine(output.Linef(output.EmojiFailure, output.StyleSuccess, "%s: added to firewall", binary)) + needsFirewallRestart = true + + default: + stdout.Out.WriteLine(output.Linef("", output.StyleSuggestion, "%s: %s", binary, strings.TrimSpace(msg))) } } } - fcmd = exec.CommandContext(ctx, "sudo", fwCmdPath, "--setglobalstate", "on") - err = fcmd.Run() - if err != nil { - return err + + if needsFirewallRestart { + restartFirewall := script. + Exec(shell.Join([]string{"sudo", firewallCmdPath, "--setglobalstate", "off"})). + Exec(shell.Join([]string{"sudo", firewallCmdPath, "--setglobalstate", "on"})) + return restartFirewall.Error() } + return nil } } @@ -172,13 +209,15 @@ func waitForInstallation(cmdNames map[string]struct{}, installed chan string, fa // Everything installed! if len(cmdNames) == 0 { progress.Complete() - stdout.Out.Write("") - stdout.Out.WriteLine(output.Linef(output.EmojiSuccess, output.StyleSuccess, "Everything installed! Booting up the system!")) - stdout.Out.Write("") err := postInstallCallback() if err != nil { return err } + + stdout.Out.Write("") + stdout.Out.WriteLine(output.Linef(output.EmojiSuccess, output.StyleSuccess, "Everything installed! Booting up the system!")) + stdout.Out.Write("") + close(okayToStart) return nil } diff --git a/go.mod b/go.mod index 041fa1dc2b9..febd0ff77fc 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/sourcegraph/sourcegraph go 1.17 require ( + bitbucket.org/creachadair/shell v0.0.7 cloud.google.com/go/kms v1.1.0 cloud.google.com/go/monitoring v1.2.0 cloud.google.com/go/profiler v0.2.0 @@ -23,6 +24,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/s3 v1.24.1 github.com/aws/smithy-go v1.11.0 github.com/beevik/etree v1.1.0 + github.com/bitfield/script v0.20.0 github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff github.com/buildkite/go-buildkite/v3 v3.0.1 github.com/cespare/xxhash v1.1.0 @@ -204,6 +206,8 @@ require ( github.com/envoyproxy/protoc-gen-validate v0.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect + github.com/itchyny/gojq v0.12.7 // indirect + github.com/itchyny/timefmt-go v0.1.3 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index 9fa4bbf2cac..bdb2de1b558 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= bazil.org/fuse v0.0.0-20180421153158-65cc252bf669/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= +bitbucket.org/creachadair/shell v0.0.7 h1:Z96pB6DkSb7F3Y3BBnJeOZH2gazyMTWlvecSD4vDqfk= +bitbucket.org/creachadair/shell v0.0.7/go.mod h1:oqtXSSvSYr4624lnnabXHaBsYW6RD80caLi2b3hJk0U= cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.25.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -378,6 +380,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitfield/script v0.20.0 h1:dqeNh8LKf3MfKN21fpuuXNNZPzLlrG6WnmScHqOt13I= +github.com/bitfield/script v0.20.0/go.mod h1:l3AZPVAtKQrL03bwh7nlNTUtgrgSWurpJSbtqspYrOA= github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= @@ -1446,6 +1450,10 @@ github.com/iris-contrib/pongo2 v0.0.1/go.mod h1:Ssh+00+3GAZqSQb30AvBRNxBx7rf0Gqw github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw= github.com/ishidawataru/sctp v0.0.0-20191218070446-00ab2ac2db07/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/ishidawataru/sctp v0.0.0-20210226210310-f2269e66cdee/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= +github.com/itchyny/gojq v0.12.7 h1:hYPTpeWfrJ1OT+2j6cvBScbhl0TkdwGM4bc66onUSOQ= +github.com/itchyny/gojq v0.12.7/go.mod h1:ZdvNHVlzPgUf8pgjnuDTmGfHA/21KoutQUJ3An/xNuw= +github.com/itchyny/timefmt-go v0.1.3 h1:7M3LGVDsqcd0VZH2U+x393obrzZisp7C0uEe921iRkU= +github.com/itchyny/timefmt-go v0.1.3/go.mod h1:0osSSCQSASBJMsIZnhAaF1C2fCBTJZXrnj37mG8/c+A= github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= diff --git a/sg.config.yaml b/sg.config.yaml index 32854aaaf1c..d105315cddd 100644 --- a/sg.config.yaml +++ b/sg.config.yaml @@ -299,7 +299,7 @@ commands: caddy: ignoreStdout: true ignoreStderr: true - cmd: .bin/caddy_${CADDY_VERSION}_$([[ "$(go env GOOS)" = "darwin" ]] && echo "mac" || echo "$(go env GOOS)")_$(go env GOARCH) run --watch --config=dev/Caddyfile + cmd: .bin/caddy_${CADDY_VERSION} run --watch --config=dev/Caddyfile install: | case "$(go env GOOS)" in linux) @@ -310,7 +310,7 @@ commands: ;; esac name="caddy_${CADDY_VERSION}_${os}_$(go env GOARCH)" - target="$PWD/.bin/$name" + target="$PWD/.bin/caddy_${CADDY_VERSION}" url="https://github.com/caddyserver/caddy/releases/download/v${CADDY_VERSION}/${name}.tar.gz" if [ ! -f "${target}" ]; then