Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

SG start bazel enhancements #59718

Merged
merged 23 commits into from
Feb 6, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fcc49fc
WIP draft
jamesmcnamara Jan 19, 2024
c12eae5
Merge branch 'main' of github.com:sourcegraph/sourcegraph into jsm/sg…
jamesmcnamara Jan 19, 2024
0f47a01
cleanup
jamesmcnamara Jan 19, 2024
1e0f25d
added output buffering
jamesmcnamara Jan 19, 2024
5a6a4b8
fixed process exiting (I think)
jamesmcnamara Jan 23, 2024
9de816c
added bazel run targets
jamesmcnamara Jan 23, 2024
016b419
Merge branch 'main' into jsm/sg-start-bazel
jamesmcnamara Jan 23, 2024
a3f3ae5
ran go mod tidy and gazelle
jamesmcnamara Jan 23, 2024
6b36048
WIP draft
jamesmcnamara Jan 19, 2024
b722598
cleanup
jamesmcnamara Jan 19, 2024
3bb5155
added output buffering
jamesmcnamara Jan 19, 2024
cd1787a
fixed process exiting (I think)
jamesmcnamara Jan 23, 2024
3bd8396
added bazel run targets
jamesmcnamara Jan 23, 2024
83cb560
ran go mod tidy and gazelle
jamesmcnamara Jan 23, 2024
a9407b8
Merge branch 'jsm/sg-start-bazel' of github.com:sourcegraph/sourcegra…
jamesmcnamara Jan 23, 2024
d94df1f
fixed installing progress bar render issue
jamesmcnamara Jan 24, 2024
0f47f02
Merge branch 'main' into jsm/sg-start-bazel
jamesmcnamara Jan 24, 2024
feeb869
Removed closes that were causing race conditions
jamesmcnamara Jan 24, 2024
f572b05
Merge branch 'main' of github.com:sourcegraph/sourcegraph into jsm/sg…
jamesmcnamara Jan 24, 2024
ebc2b71
logging extracted from ibazel output
jamesmcnamara Jan 25, 2024
291c219
only log first and relevant errors
jamesmcnamara Jan 25, 2024
737bcbb
Merge branch 'main' into jsm/sg-start-bazel
jamesmcnamara Feb 5, 2024
72958b9
removed slow testing command
jamesmcnamara Feb 6, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .bazel_fix_commands.json
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": []
}
]
Comment on lines +1 to +17
Copy link
Contributor Author

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.

19 changes: 19 additions & 0 deletions dev/bazel_configure_accept_changes.sh
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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
9 changes: 4 additions & 5 deletions dev/sg/internal/run/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
load("//dev:go_defs.bzl", "go_test")
load("@io_bazel_rules_go//go:def.bzl", "go_library")
load("//dev:go_defs.bzl", "go_test")

go_library(
name = "run",
srcs = [
"bazel_build.go",
"bazel_command.go",
"command.go",
"helpers.go",
"ibazel.go",
"installer.go",
"logger.go",
"pid.go",
"prefix_suffix_saver.go",
"run.go",
"run_bazel.go",
"sgconfig_command.go",
],
importpath = "github.com/sourcegraph/sourcegraph/dev/sg/internal/run",
visibility = ["//dev/sg:__subpackages__"],
Expand All @@ -27,10 +27,9 @@ go_library(
"//lib/errors",
"//lib/output",
"//lib/process",
"@com_github_grafana_regexp//:regexp",
"@com_github_nxadm_tail//:tail",
"@com_github_rjeczalik_notify//:notify",
"@com_github_sourcegraph_conc//pool",
"@org_golang_x_sync//semaphore",
],
)

Expand Down
64 changes: 0 additions & 64 deletions dev/sg/internal/run/bazel_build.go

This file was deleted.

169 changes: 63 additions & 106 deletions dev/sg/internal/run/bazel_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: shouldn't this be watchedPaths? I intuitively read watchPaths as "I will watch those paths" not as "get me the paths that I should watch"

// 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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Watch(ctx context.Context) ... ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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()
}
Loading