@@ -3,150 +3,123 @@ package run
3
3
import (
4
4
"context"
5
5
"fmt"
6
- "io"
7
6
"os/exec"
7
+ "strings"
8
8
9
9
"github.com/rjeczalik/notify"
10
+
10
11
"github.com/sourcegraph/sourcegraph/dev/sg/internal/secrets"
11
- "github.com/sourcegraph/sourcegraph/dev/sg/internal/std"
12
- "github.com/sourcegraph/sourcegraph/lib/errors"
13
- "github.com/sourcegraph/sourcegraph/lib/output"
14
- "github.com/sourcegraph/sourcegraph/lib/process"
15
12
)
16
13
17
14
// A BazelCommand is a command definition for sg run/start that uses
18
15
// bazel under the hood. It will handle restarting itself autonomously,
19
16
// as long as iBazel is running and watch that specific target.
20
17
type BazelCommand struct {
21
- Name string
22
- Description string `yaml:"description"`
23
- Target string `yaml:"target"`
24
- Args string `yaml:"args"`
25
- PreCmd string `yaml:"precmd"`
26
- Env map [string ]string `yaml:"env"`
27
- IgnoreStdout bool `yaml:"ignoreStdout"`
28
- IgnoreStderr bool `yaml:"ignoreStderr"`
18
+ Name string
19
+ Description string `yaml:"description"`
20
+ Target string `yaml:"target"`
21
+ Args string `yaml:"args"`
22
+ PreCmd string `yaml:"precmd"`
23
+ Env map [string ]string `yaml:"env"`
24
+ IgnoreStdout bool `yaml:"ignoreStdout"`
25
+ IgnoreStderr bool `yaml:"ignoreStderr"`
26
+ ContinueWatchOnExit bool `yaml:"continueWatchOnExit"`
27
+ // Preamble is a short and visible message, displayed when the command is launched.
28
+ Preamble string `yaml:"preamble"`
29
29
ExternalSecrets map [string ]secrets.ExternalSecret `yaml:"external_secrets"`
30
- }
31
30
32
- func ( bc * BazelCommand ) BinLocation () ( string , error ) {
33
- return binLocation ( bc . Target )
31
+ // RunTarget specifies a target that should be run via `bazel run $RunTarget` instead of directly executing the binary.
32
+ RunTarget string `yaml:"runTarget"`
34
33
}
35
34
36
- func (bc * BazelCommand ) watch (ctx context.Context ) (<- chan struct {}, error ) {
37
- // Grab the location of the binary in bazel-out.
38
- binLocation , err := bc .BinLocation ()
39
- if err != nil {
40
- return nil , err
41
- }
35
+ func (bc BazelCommand ) GetName () string {
36
+ return bc .Name
37
+ }
42
38
43
- // Set up the watcher.
44
- restart := make (chan struct {})
45
- events := make (chan notify.EventInfo , 1 )
46
- if err := notify .Watch (binLocation , events , notify .All ); err != nil {
47
- return nil , err
48
- }
39
+ func (bc BazelCommand ) GetContinueWatchOnExit () bool {
40
+ return bc .ContinueWatchOnExit
41
+ }
49
42
50
- // Start watching for a freshly compiled version of the binary.
51
- go func () {
52
- defer close (events )
53
- defer notify .Stop (events )
54
-
55
- for {
56
- select {
57
- case <- ctx .Done ():
58
- return
59
- case e := <- events :
60
- if e .Event () != notify .Remove {
61
- restart <- struct {}{}
62
- }
63
- }
43
+ func (bc BazelCommand ) GetEnv () map [string ]string {
44
+ return bc .Env
45
+ }
64
46
65
- }
66
- }()
47
+ func (bc BazelCommand ) GetIgnoreStdout () bool {
48
+ return bc .IgnoreStdout
49
+ }
67
50
68
- return restart , nil
51
+ func (bc BazelCommand ) GetIgnoreStderr () bool {
52
+ return bc .IgnoreStderr
69
53
}
70
54
71
- func (bc * BazelCommand ) Start (ctx context.Context , dir string , parentEnv map [string ]string ) error {
72
- std .Out .WriteLine (output .Styledf (output .StylePending , "Running %s..." , bc .Name ))
55
+ func (bc BazelCommand ) GetPreamble () string {
56
+ return bc .Preamble
57
+ }
73
58
74
- // Run the binary for the first time.
75
- cancel , err := bc . start ( ctx , dir , parentEnv )
59
+ func ( bc BazelCommand ) GetBinaryLocation () ( string , error ) {
60
+ baseOutput , err := outputPath ( )
76
61
if err != nil {
77
- return errors . Wrapf ( err , "failed to start Bazel command %q " , bc . Name )
62
+ return " " , err
78
63
}
64
+ // Trim "bazel-out" because the next bazel query will include it.
65
+ outputPath := strings .TrimSuffix (strings .TrimSpace (string (baseOutput )), "bazel-out" )
79
66
80
- // Restart when the binary change.
81
- wantRestart , err := bc .watch (ctx )
67
+ // Get the binary from the specific target.
68
+ cmd := exec .Command ("bazel" , "cquery" , bc .Target , "--output=files" )
69
+ baseOutput , err = cmd .Output ()
82
70
if err != nil {
83
- return err
71
+ return "" , err
84
72
}
73
+ binPath := strings .TrimSpace (string (baseOutput ))
85
74
86
- // Wait forever until we're asked to stop or that restarting returns an error.
87
- for {
88
- select {
89
- case <- ctx .Done ():
90
- return ctx .Err ()
91
- case <- wantRestart :
92
- std .Out .WriteLine (output .Styledf (output .StylePending , "Restarting %s..." , bc .Name ))
93
- cancel ()
94
- cancel , err = bc .start (ctx , dir , parentEnv )
95
- if err != nil {
96
- return err
97
- }
98
- }
99
- }
75
+ return fmt .Sprintf ("%s%s" , outputPath , binPath ), nil
100
76
}
101
77
102
- func (bc * BazelCommand ) start (ctx context.Context , dir string , parentEnv map [string ]string ) (func (), error ) {
103
- binLocation , err := bc .BinLocation ()
104
- if err != nil {
105
- return nil , err
106
- }
78
+ func (bc BazelCommand ) GetExternalSecrets () map [string ]secrets.ExternalSecret {
79
+ return bc .ExternalSecrets
80
+ }
107
81
108
- sc := & startedCmd {
109
- stdoutBuf : & prefixSuffixSaver {N : 32 << 10 },
110
- stderrBuf : & prefixSuffixSaver {N : 32 << 10 },
82
+ func (bc BazelCommand ) watchPaths () ([]string , error ) {
83
+ // If no target is defined, there is nothing to be built and watched
84
+ if bc .Target == "" {
85
+ return nil , nil
111
86
}
112
-
113
- commandCtx , cancel := context .WithCancel (ctx )
114
- sc .cancel = cancel
115
- sc .Cmd = exec .CommandContext (commandCtx , "bash" , "-c" , fmt .Sprintf ("%s\n %s" , bc .PreCmd , binLocation ))
116
- sc .Cmd .Dir = dir
117
-
118
- secretsEnv , err := getSecrets (ctx , bc .Name , bc .ExternalSecrets )
87
+ // Grab the location of the binary in bazel-out.
88
+ binLocation , err := bc .GetBinaryLocation ()
119
89
if err != nil {
120
- std .Out .WriteLine (output .Styledf (output .StyleWarning , "[%s] %s %s" ,
121
- bc .Name , output .EmojiFailure , err .Error ()))
90
+ return nil , err
122
91
}
92
+ return []string {binLocation }, nil
123
93
124
- sc . Cmd . Env = makeEnv ( parentEnv , secretsEnv , bc . Env )
94
+ }
125
95
126
- var stdoutWriter , stderrWriter io.Writer
127
- logger := newCmdLogger (commandCtx , bc .Name , std .Out .Output )
128
- if bc .IgnoreStdout {
129
- std .Out .WriteLine (output .Styledf (output .StyleSuggestion , "Ignoring stdout of %s" , bc .Name ))
130
- stdoutWriter = sc .stdoutBuf
131
- } else {
132
- stdoutWriter = io .MultiWriter (logger , sc .stdoutBuf )
133
- }
134
- if bc .IgnoreStderr {
135
- std .Out .WriteLine (output .Styledf (output .StyleSuggestion , "Ignoring stderr of %s" , bc .Name ))
136
- stderrWriter = sc .stderrBuf
96
+ func (bc BazelCommand ) StartWatch (ctx context.Context ) (<- chan struct {}, error ) {
97
+ if watchPaths , err := bc .watchPaths (); err != nil {
98
+ return nil , err
137
99
} else {
138
- stderrWriter = io .MultiWriter (logger , sc .stderrBuf )
100
+ // skip remove events as we don't care about files being removed, we only
101
+ // want to know when the binary has been rebuilt
102
+ return WatchPaths (ctx , watchPaths , notify .Remove )
139
103
}
104
+ }
140
105
141
- eg , err := process .PipeOutputUnbuffered (ctx , sc .Cmd , stdoutWriter , stderrWriter )
142
- if err != nil {
143
- return nil , err
106
+ func (bc BazelCommand ) GetExecCmd (ctx context.Context ) (* exec.Cmd , error ) {
107
+ var cmd string
108
+ var err error
109
+ if bc .RunTarget != "" {
110
+ cmd = "bazel run " + bc .RunTarget
111
+ } else {
112
+ if cmd , err = bc .GetBinaryLocation (); err != nil {
113
+ return nil , err
114
+ }
144
115
}
145
- sc .outEg = eg
146
116
147
- if err := sc .Start (); err != nil {
148
- return nil , err
149
- }
117
+ return exec .CommandContext (ctx , "bash" , "-c" , fmt .Sprintf ("%s\n %s" , bc .PreCmd , cmd )), nil
118
+ }
150
119
151
- return cancel , nil
120
+ func outputPath () ([]byte , error ) {
121
+ // Get the output directory from Bazel, which varies depending on which OS
122
+ // we're running against.
123
+ cmd := exec .Command ("bazel" , "info" , "output_path" )
124
+ return cmd .Output ()
152
125
}
0 commit comments