fix(sg): make sg gen output more readable (#64406)

Closes DINF-78

The output of `sg gen` is a bit hard to read when there's an error, this
is because the new line character `\n` isn't rendered as a new line. It
turns out the `%q` formatting directive used to quote a string doesn't
render the `\n` character as a new line.

| Before |
|---|
| ![CleanShot 2024-08-12 at 11 17
57@2x](https://github.com/user-attachments/assets/e03ec503-e437-4b68-80b3-fe34ac8848fb)
|

| After  |
|---|
| ![CleanShot 2024-08-12 at 10 53
35@2x](https://github.com/user-attachments/assets/5b7aac63-27b6-4de0-9c56-3b739f0ee0f9)
|

I also added a func to extract error messages from a bazel command to
avoid long output message when a bazel command fails and give the user
relevant messages related to the error.

| Before  |
|---|


https://github.com/user-attachments/assets/2d029ec1-5804-41bf-a675-8642e169ea80


| After  |
|---|
| ![CleanShot 2024-08-12 at 14 45
59@2x](https://github.com/user-attachments/assets/7d567fd6-de37-48aa-b2b5-03dc591fc77a)
|

## Test plan

<!-- REQUIRED; info at
https://docs-legacy.sourcegraph.com/dev/background-information/testing_principles
-->

* Manual testing

## Changelog

<!-- OPTIONAL; info at
https://www.notion.so/sourcegraph/Writing-a-changelog-entry-dd997f411d524caabf0d8d38a24a878c
-->
This commit is contained in:
Bolaji Olajide 2024-08-13 12:22:21 +01:00 committed by GitHub
parent 67f30a9d7a
commit 4d57eb1188
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 64 additions and 18 deletions

View File

@ -654,7 +654,7 @@ var docsCommand = &cli.Command{
Usage: "Render reference documentation for build pipeline types",
Action: func(ctx *cli.Context) error {
cmd := exec.Command("go", "run", "./dev/ci/gen-pipeline.go", "-docs")
out, err := run.InRoot(cmd)
out, err := run.InRoot(cmd, run.InRootArgs{})
if err != nil {
return err
}

View File

@ -56,7 +56,8 @@ var allGenerateTargets = generateTargets{
}
func generateBazelRunner(ctx context.Context, args []string) *generate.Report {
return generate.RunScript("bazel run //dev:write_all_generated")(ctx, args)
runnerFunc := generate.RunScript("bazel run //dev:write_all_generated", true)
return runnerFunc(ctx, args)
}
func generateGoRunner(ctx context.Context, args []string) *generate.Report {

View File

@ -34,10 +34,12 @@ type Target struct {
// RunScript runs the given script from the root of sourcegraph/sourcegraph.
// If arguments are to be to passed down the script, they should be incorporated
// in the script variable.
func RunScript(command string) Runner {
func RunScript(command string, extractBazelError bool) Runner {
return func(ctx context.Context, args []string) *Report {
start := time.Now()
out, err := run.BashInRoot(ctx, command, nil)
out, err := run.BashInRoot(ctx, command, run.BashInRootArgs{
ExtractBazelError: extractBazelError,
})
return &Report{
Output: out,
Err: err,

View File

@ -327,13 +327,13 @@ func setupLocalDatabase(databaseName string) (_ func(error) error, err error) {
createLocalDatabase := func() error {
cmd := exec.Command("createdb", databaseName)
_, err := run.InRoot(cmd)
_, err := run.InRoot(cmd, run.InRootArgs{})
return err
}
dropLocalDatabase := func() error {
cmd := exec.Command("dropdb", databaseName)
_, err := run.InRoot(cmd)
_, err := run.InRoot(cmd, run.InRootArgs{})
return err
}

View File

@ -26,6 +26,7 @@ go_library(
"//dev/sg/interrupt",
"//dev/sg/root",
"//internal/download",
"//internal/lazyregexp",
"//lib/errors",
"//lib/output",
"//lib/process",

View File

@ -108,9 +108,11 @@ func (cmd Command) hasBashInstaller() bool {
}
func (cmd Command) bashInstall(ctx context.Context, parentEnv map[string]string) error {
output, err := BashInRoot(ctx, cmd.Install, makeEnv(parentEnv, cmd.Config.Env))
out, err := BashInRoot(ctx, cmd.Install, BashInRootArgs{
Env: makeEnv(parentEnv, cmd.Config.Env),
})
if err != nil {
return installErr{cmdName: cmd.Config.Name, output: output, originalErr: err}
return installErr{cmdName: cmd.Config.Name, output: out, originalErr: err}
}
return nil
}

View File

@ -9,6 +9,7 @@ import (
"strings"
"github.com/sourcegraph/sourcegraph/dev/sg/root"
"github.com/sourcegraph/sourcegraph/internal/lazyregexp"
)
func GitCmd(args ...string) (string, error) {
@ -19,11 +20,11 @@ func GitCmd(args ...string) (string, error) {
// And also not any other, because they can mess up output, change defaults, .. which can do unexpected things.
"GIT_CONFIG=/dev/null")
return InRoot(cmd)
return InRoot(cmd, InRootArgs{})
}
func DockerCmd(args ...string) (string, error) {
return InRoot(exec.Command("docker", args...))
return InRoot(exec.Command("docker", args...), InRootArgs{})
}
type errorWithoutOutputer interface {
@ -37,7 +38,8 @@ type cmdInRootErr struct {
}
func (e cmdInRootErr) Error() string {
return fmt.Sprintf("'%s' failed: err = %q, output = %q", strings.Join(e.args, " "), e.err.Error(), e.output)
return fmt.Sprintf(`'%s' failed: err = "%s", output = "%s"`, strings.Join(e.args, " "), e.err.Error(), e.output)
}
func (e cmdInRootErr) ErrorWithoutOutput() string {
@ -46,7 +48,11 @@ func (e cmdInRootErr) ErrorWithoutOutput() string {
func (e cmdInRootErr) Unwrap() error { return e.err }
func InRoot(cmd *exec.Cmd) (string, error) {
type InRootArgs struct {
ExtractBazelError bool
}
func InRoot(cmd *exec.Cmd, args InRootArgs) (string, error) {
repoRoot, err := root.RepositoryRoot()
if err != nil {
return "", err
@ -55,12 +61,33 @@ func InRoot(cmd *exec.Cmd) (string, error) {
cmd.Dir = repoRoot
out, err := cmd.CombinedOutput()
if err != nil {
return string(out), cmdInRootErr{err: err, args: cmd.Args, output: string(out)}
output := string(out)
if args.ExtractBazelError {
// this is still experimental and currently only works for bazel errors
output = bazelErrorExtractor(out)
}
return string(out), cmdInRootErr{err: err, args: cmd.Args, output: output}
}
return string(out), nil
}
// `(?m)` enables multiline mode, `^` matches the start of each line
var errorRegex = lazyregexp.New(`(?m)^ERROR:.*$`)
func bazelErrorExtractor(input []byte) string {
// Find all matches
matches := errorRegex.FindAll(input, -1)
// Convert [][]byte to string
var errorLines string
for _, match := range matches {
errorLines += string(match) + "\n"
}
return errorLines
}
func SplitOutputInRoot(cmd *exec.Cmd, stdout, stderr io.Writer) error {
cmd.Stdout = stdout
cmd.Stderr = stderr
@ -72,10 +99,17 @@ func SplitOutputInRoot(cmd *exec.Cmd, stdout, stderr io.Writer) error {
return cmd.Run()
}
func BashInRoot(ctx context.Context, cmd string, env []string) (string, error) {
type BashInRootArgs struct {
Env []string
ExtractBazelError bool
}
func BashInRoot(ctx context.Context, cmd string, args BashInRootArgs) (string, error) {
c := exec.CommandContext(ctx, "bash", "-c", cmd)
c.Env = env
return InRoot(c)
c.Env = args.Env
return InRoot(c, InRootArgs{
ExtractBazelError: args.ExtractBazelError,
})
}
func TrimResult(s string, err error) (string, error) {

View File

@ -103,7 +103,9 @@ func (d *diagnosticRunner) Run(ctx context.Context) DiagnosticReport {
for group, diagnostics := range d.diagnostics.Diagnostic {
d.reporter.WriteLine(output.Emojif("💊", "Running %s diagnostics", group))
for _, diagnostic := range diagnostics {
out, err := run.BashInRoot(ctx, diagnostic.Cmd, env)
out, err := run.BashInRoot(ctx, diagnostic.Cmd, run.BashInRootArgs{
Env: env,
})
diag := diagnostic
report.Add(group, &DiagnosticResult{
&diag,

View File

@ -93,7 +93,7 @@ func changelogExec(ctx *cli.Context) error {
gitLog := exec.Command("git", append(logArgs, "--", "./dev/sg")...)
gitLog.Env = os.Environ()
out, err := run.InRoot(gitLog)
out, err := run.InRoot(gitLog, run.InRootArgs{})
if err != nil {
return err
}

View File

@ -85,6 +85,10 @@ func (r *Regexp) FindAllIndex(b []byte, n int) [][]int {
return r.Re().FindAllIndex(b, n)
}
func (r *Regexp) FindAll(b []byte, n int) [][]byte {
return r.Re().FindAll(b, n)
}
func (r *Regexp) Match(b []byte) bool {
return r.Re().Match(b)
}