-
Notifications
You must be signed in to change notification settings - Fork 1.3k
SG start bazel enhancements #59718
SG start bazel enhancements #59718
Changes from 3 commits
fcc49fc
c12eae5
0f47a01
1e0f25d
5a6a4b8
9de816c
016b419
a3f3ae5
6b36048
b722598
3bb5155
cd1787a
3bd8396
83cb560
a9407b8
d94df1f
0f47f02
feeb869
f572b05
ebc2b71
291c219
737bcbb
72958b9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,17 @@ | ||
[] | ||
[ | ||
{ | ||
"regex": "^Check that imports in Go sources match importpath attributes in deps.$", | ||
"command": "./dev/bazel_configure_accept_changes.sh", | ||
"args": [] | ||
}, | ||
{ | ||
"regex": "missing input file", | ||
"command": "./dev/bazel_configure_accept_changes.sh", | ||
"args": [] | ||
}, | ||
{ | ||
"regex": ": undefined:", | ||
"command": "./dev/bazel_configure_accept_changes.sh", | ||
"args": [] | ||
} | ||
] | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
#! /bin/bash | ||
|
||
# Run bazel configure and if the error code is 110, exit with error code 0 | ||
# This is because 110 means that configuration files were successfully | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like the sentence is cut over here :D |
||
# Can be used by processes which want to run configuration as an auto-fix | ||
# and expect a 0 exit code | ||
bazel configure | ||
exit_code=$? | ||
|
||
if [ $exit_code -eq 0 ]; then | ||
echo "No configuration changes made" | ||
exit 0 | ||
elif [ $exit_code -eq 110 ]; then | ||
echo "Bazel configuration completed" | ||
exit 0 | ||
else | ||
echo "Unknown error" | ||
exit $exit_code | ||
fi |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,150 +3,107 @@ package run | |
import ( | ||
"context" | ||
"fmt" | ||
"io" | ||
"os/exec" | ||
"strings" | ||
|
||
"github.com/rjeczalik/notify" | ||
"github.com/sourcegraph/sourcegraph/dev/sg/internal/secrets" | ||
"github.com/sourcegraph/sourcegraph/dev/sg/internal/std" | ||
"github.com/sourcegraph/sourcegraph/lib/errors" | ||
"github.com/sourcegraph/sourcegraph/lib/output" | ||
"github.com/sourcegraph/sourcegraph/lib/process" | ||
) | ||
|
||
// A BazelCommand is a command definition for sg run/start that uses | ||
// bazel under the hood. It will handle restarting itself autonomously, | ||
// as long as iBazel is running and watch that specific target. | ||
type BazelCommand struct { | ||
Name string | ||
Description string `yaml:"description"` | ||
Target string `yaml:"target"` | ||
Args string `yaml:"args"` | ||
PreCmd string `yaml:"precmd"` | ||
Env map[string]string `yaml:"env"` | ||
IgnoreStdout bool `yaml:"ignoreStdout"` | ||
IgnoreStderr bool `yaml:"ignoreStderr"` | ||
Name string | ||
Description string `yaml:"description"` | ||
Target string `yaml:"target"` | ||
Args string `yaml:"args"` | ||
PreCmd string `yaml:"precmd"` | ||
Env map[string]string `yaml:"env"` | ||
IgnoreStdout bool `yaml:"ignoreStdout"` | ||
IgnoreStderr bool `yaml:"ignoreStderr"` | ||
ContinueWatchOnExit bool `yaml:"continueWatchOnExit"` | ||
// Preamble is a short and visible message, displayed when the command is launched. | ||
Preamble string `yaml:"preamble"` | ||
ExternalSecrets map[string]secrets.ExternalSecret `yaml:"external_secrets"` | ||
} | ||
|
||
func (bc *BazelCommand) BinLocation() (string, error) { | ||
return binLocation(bc.Target) | ||
func (bc BazelCommand) GetName() string { | ||
return bc.Name | ||
} | ||
|
||
func (bc *BazelCommand) watch(ctx context.Context) (<-chan struct{}, error) { | ||
// Grab the location of the binary in bazel-out. | ||
binLocation, err := bc.BinLocation() | ||
if err != nil { | ||
return nil, err | ||
} | ||
func (bc BazelCommand) GetContinueWatchOnExit() bool { | ||
return bc.ContinueWatchOnExit | ||
} | ||
|
||
// Set up the watcher. | ||
restart := make(chan struct{}) | ||
events := make(chan notify.EventInfo, 1) | ||
if err := notify.Watch(binLocation, events, notify.All); err != nil { | ||
return nil, err | ||
} | ||
func (bc BazelCommand) GetEnv() map[string]string { | ||
return bc.Env | ||
} | ||
|
||
// Start watching for a freshly compiled version of the binary. | ||
go func() { | ||
defer close(events) | ||
defer notify.Stop(events) | ||
|
||
for { | ||
select { | ||
case <-ctx.Done(): | ||
return | ||
case e := <-events: | ||
if e.Event() != notify.Remove { | ||
restart <- struct{}{} | ||
} | ||
} | ||
|
||
} | ||
}() | ||
|
||
return restart, nil | ||
func (bc BazelCommand) GetIgnoreStdout() bool { | ||
return bc.IgnoreStdout | ||
} | ||
|
||
func (bc *BazelCommand) Start(ctx context.Context, dir string, parentEnv map[string]string) error { | ||
std.Out.WriteLine(output.Styledf(output.StylePending, "Running %s...", bc.Name)) | ||
func (bc BazelCommand) GetIgnoreStderr() bool { | ||
return bc.IgnoreStderr | ||
} | ||
|
||
// Run the binary for the first time. | ||
cancel, err := bc.start(ctx, dir, parentEnv) | ||
if err != nil { | ||
return errors.Wrapf(err, "failed to start Bazel command %q", bc.Name) | ||
} | ||
func (bc BazelCommand) GetPreamble() string { | ||
return bc.Preamble | ||
} | ||
|
||
// Restart when the binary change. | ||
wantRestart, err := bc.watch(ctx) | ||
func (bc BazelCommand) GetBinaryLocation() (string, error) { | ||
baseOutput, err := outputPath() | ||
if err != nil { | ||
return err | ||
return "", err | ||
} | ||
// Trim "bazel-out" because the next bazel query will include it. | ||
outputPath := strings.TrimSuffix(strings.TrimSpace(string(baseOutput)), "bazel-out") | ||
|
||
// Wait forever until we're asked to stop or that restarting returns an error. | ||
for { | ||
select { | ||
case <-ctx.Done(): | ||
return ctx.Err() | ||
case <-wantRestart: | ||
std.Out.WriteLine(output.Styledf(output.StylePending, "Restarting %s...", bc.Name)) | ||
cancel() | ||
cancel, err = bc.start(ctx, dir, parentEnv) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
} | ||
|
||
func (bc *BazelCommand) start(ctx context.Context, dir string, parentEnv map[string]string) (func(), error) { | ||
binLocation, err := bc.BinLocation() | ||
// Get the binary from the specific target. | ||
cmd := exec.Command("bazel", "cquery", bc.Target, "--output=files") | ||
baseOutput, err = cmd.Output() | ||
if err != nil { | ||
return nil, err | ||
return "", err | ||
} | ||
binPath := strings.TrimSpace(string(baseOutput)) | ||
|
||
sc := &startedCmd{ | ||
stdoutBuf: &prefixSuffixSaver{N: 32 << 10}, | ||
stderrBuf: &prefixSuffixSaver{N: 32 << 10}, | ||
} | ||
return fmt.Sprintf("%s%s", outputPath, binPath), nil | ||
} | ||
|
||
commandCtx, cancel := context.WithCancel(ctx) | ||
sc.cancel = cancel | ||
sc.Cmd = exec.CommandContext(commandCtx, "bash", "-c", fmt.Sprintf("%s\n%s", bc.PreCmd, binLocation)) | ||
sc.Cmd.Dir = dir | ||
func (bc BazelCommand) GetExternalSecrets() map[string]secrets.ExternalSecret { | ||
return bc.ExternalSecrets | ||
} | ||
|
||
secretsEnv, err := getSecrets(ctx, bc.Name, bc.ExternalSecrets) | ||
func (bc BazelCommand) watchPaths() ([]string, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: shouldn't this be |
||
// Grab the location of the binary in bazel-out. | ||
binLocation, err := bc.GetBinaryLocation() | ||
if err != nil { | ||
std.Out.WriteLine(output.Styledf(output.StyleWarning, "[%s] %s %s", | ||
bc.Name, output.EmojiFailure, err.Error())) | ||
return nil, err | ||
} | ||
return []string{binLocation}, nil | ||
|
||
sc.Cmd.Env = makeEnv(parentEnv, secretsEnv, bc.Env) | ||
} | ||
|
||
var stdoutWriter, stderrWriter io.Writer | ||
logger := newCmdLogger(commandCtx, bc.Name, std.Out.Output) | ||
if bc.IgnoreStdout { | ||
std.Out.WriteLine(output.Styledf(output.StyleSuggestion, "Ignoring stdout of %s", bc.Name)) | ||
stdoutWriter = sc.stdoutBuf | ||
} else { | ||
stdoutWriter = io.MultiWriter(logger, sc.stdoutBuf) | ||
} | ||
if bc.IgnoreStderr { | ||
std.Out.WriteLine(output.Styledf(output.StyleSuggestion, "Ignoring stderr of %s", bc.Name)) | ||
stderrWriter = sc.stderrBuf | ||
func (bc BazelCommand) StartWatch(ctx context.Context) (<-chan struct{}, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would love to but but both Command and BazelCommand use Watch for a local field. |
||
if watchPaths, err := bc.watchPaths(); err != nil { | ||
return nil, err | ||
} else { | ||
stderrWriter = io.MultiWriter(logger, sc.stderrBuf) | ||
return WatchPaths(ctx, watchPaths) | ||
} | ||
} | ||
|
||
eg, err := process.PipeOutputUnbuffered(ctx, sc.Cmd, stdoutWriter, stderrWriter) | ||
func (bc BazelCommand) GetExec(ctx context.Context) (*exec.Cmd, error) { | ||
binLocation, err := bc.GetBinaryLocation() | ||
if err != nil { | ||
return nil, err | ||
} | ||
sc.outEg = eg | ||
|
||
if err := sc.Start(); err != nil { | ||
return nil, err | ||
} | ||
return exec.CommandContext(ctx, "bash", "-c", fmt.Sprintf("%s\n%s", bc.PreCmd, binLocation)), nil | ||
} | ||
|
||
return cancel, nil | ||
func outputPath() ([]byte, error) { | ||
// Get the output directory from Bazel, which varies depending on which OS | ||
// we're running against. | ||
cmd := exec.Command("bazel", "info", "output_path") | ||
return cmd.Output() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contains patterns of bazel output and the command to run to try to auto fix it. iBazel will use this to to try to auto-resolve build problems.