Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RB] Show full child command in UI #7402

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions app/invocation/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ ts_library(
deps = [
"//app/components/link",
"//app/format",
"//app/invocation:invocation_model",
"//proto:invocation_status_ts_proto",
"//proto:invocation_ts_proto",
"@npm//@types/react",
Expand Down
31 changes: 28 additions & 3 deletions app/invocation/child_invocation_card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { invocation } from "../../proto/invocation_ts_proto";
import { CheckCircle, PlayCircle, XCircle, CircleSlash } from "lucide-react";
import Link from "../components/link/link";
import { invocation_status } from "../../proto/invocation_status_ts_proto";
import InvocationModel from "./invocation_model";

type CommandStatus = "failed" | "succeeded" | "in-progress" | "not-run";

Expand Down Expand Up @@ -52,14 +53,38 @@ export default class ChildInvocationCard extends React.Component<ChildInvocation
}
}

private simplifyCommandLine(commandLine: string): string {
// Remove the bazel command string
if (commandLine.startsWith("bazel ")) {
commandLine = commandLine.slice("bazel ".length);
}

// Strip all --remote_header flags
commandLine = commandLine.replace(/\s*--remote_header=[^\s]+/g, "");
// Strip the options we manually added
commandLine = commandLine.replace(" --config=buildbuddy_bes_backend", "");
commandLine = commandLine.replace(" --config=buildbuddy_bes_results_url", "");
commandLine = commandLine.replace(" --config=buildbuddy_remote_cache", "");
commandLine = commandLine.replace(/\s*--invocation_id=[^\s]+/g, "");

return commandLine;
Comment on lines +57 to +70
Copy link
Member

Choose a reason for hiding this comment

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

We have EXPLICIT_COMMAND_LINE that is intended as a way to override the bazel command displayed in the UI in cases where there is some extra arg fiddling going on (e.g. the CLI) - can we set that build metadata and use it directly instead of "undoing" the arg fiddling here?

Copy link
Contributor Author

@maggie-lou maggie-lou Oct 11, 2024

Choose a reason for hiding this comment

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

I realized that the redactor strips EXPLICIT_COMMAND_LINE from the build metadata, so that is actually an ineffective way to override the bazel command displayed in the UI (Here's a PR to remove the code from the CLI. Alternatively, we could add it back and risk there being places we are incorrectly redacting it, which was the original impetus to remove it)

Copy link
Member

@bduffany bduffany Oct 11, 2024

Choose a reason for hiding this comment

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

the redactor strips EXPLICIT_COMMAND_LINE from the build metadata

I think the linked code is stripping one of the CommandLine events, not the BuildMetadata event?

Copy link
Member

@bduffany bduffany Oct 11, 2024

Choose a reason for hiding this comment

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

after a closer look, that linked code is stripping any nested occurrences of --build_metadata=EXPLICIT_COMMAND_LINE= within the explicit command line (which shouldn't normally happen), but the EXPLICIT_COMMAND_LINE build metadata is still preserved in the BuildMetadata event

}

render() {
const status = this.getStatus();
const inv = this.props.invocation;
const command = `${inv.command} ${inv.pattern.join(" ")}`;
const invModel = new InvocationModel(inv);

const status = this.getStatus();
let command = invModel.explicitCommandLine();
if (command == "") {
command = `${inv.command} ${inv.pattern.join(" ")}`;
}
command = this.simplifyCommandLine(command);

return (
<Link
className={`child-invocation-card status-${status} ${this.isClickable(status) ? "clickable" : ""}`}
href={this.isClickable(status) ? `/invocation/${this.props.invocation.invocationId}` : undefined}>
href={this.isClickable(status) ? `/invocation/${inv.invocationId}` : undefined}>
<div className="icon-container">{this.renderStatusIcon(status)}</div>
<div className="command">{command}</div>
<div className="duration">{this.getDurationLabel(status)}</div>
Expand Down
1 change: 1 addition & 0 deletions app/invocation/invocation_model.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,7 @@ export default class InvocationModel {
}

explicitCommandLine() {
// TODO(Maggie): Clean up - will always be redacted
// We allow overriding EXPLICIT_COMMAND_LINE to enable tools that wrap bazel
// to append bazel args but still preserve the appearance of the original
// command line. The effective command line can still be used to see the
Expand Down
5 changes: 3 additions & 2 deletions cli/remotebazel/remotebazel.go
Original file line number Diff line number Diff line change
Expand Up @@ -1042,8 +1042,9 @@ func parseArgs(commandLineArgs []string) (bazelArgs []string, execArgs []string,
// app backend as the remote runner.
bazelArgs = arg.Remove(bazelArgs, "bes_backend")
bazelArgs = arg.Remove(bazelArgs, "remote_cache")
bazelArgs = append(bazelArgs, "--bes_backend="+*remoteRunner)
bazelArgs = append(bazelArgs, "--remote_cache="+*remoteRunner)
bazelArgs = append(bazelArgs, "--config=buildbuddy_bes_backend")
bazelArgs = append(bazelArgs, "--config=buildbuddy_bes_results_url")
bazelArgs = append(bazelArgs, "--config=buildbuddy_remote_cache")

return bazelArgs, execArgs, nil
}
Expand Down
19 changes: 16 additions & 3 deletions server/backends/invocationdb/invocationdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package invocationdb

import (
"context"
"database/sql"
"errors"
"strings"
"time"
Expand Down Expand Up @@ -181,10 +182,22 @@ func (d *InvocationDB) LookupInvocation(ctx context.Context, invocationID string
return ti, nil
}

func (d *InvocationDB) LookupChildInvocations(ctx context.Context, parentRunID string) ([]*tables.Invocation, error) {
func (d *InvocationDB) LookupChildInvocations(ctx context.Context, parentRunID string) ([]string, error) {
rq := d.h.NewQuery(ctx, "invocationdb_get_child_invocations").Raw(
`SELECT * FROM "Invocations" WHERE parent_run_id = ? ORDER BY created_at_usec`, parentRunID)
return db.ScanAll(rq, &tables.Invocation{})
`SELECT invocation_id FROM "Invocations" WHERE parent_run_id = ? ORDER BY created_at_usec`, parentRunID)
iids := make([]string, 0)
err := rq.IterateRaw(func(ctx context.Context, row *sql.Rows) error {
var iid *string
if err := row.Scan(&iid); err != nil {
return err
}
iids = append(iids, *iid)
return nil
})
if err != nil {
return nil, err
}
return iids, nil
}

func (d *InvocationDB) LookupGroupFromInvocation(ctx context.Context, invocationID string) (*tables.Group, error) {
Expand Down
144 changes: 73 additions & 71 deletions server/build_event_protocol/build_event_handler/build_event_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1428,7 +1428,6 @@ func LookupInvocationWithCallback(ctx context.Context, env environment.Env, iid
}

invocation := invocationdb.TableInvocationToProto(ti)
streamID := GetStreamIdFromInvocationIdAndAttempt(iid, ti.Attempt)

var scoreCard *capb.ScoreCard
eg, ctx := errgroup.WithContext(ctx)
Expand All @@ -1453,87 +1452,90 @@ func LookupInvocationWithCallback(ctx context.Context, env environment.Env, iid
})

eg.Go(func() error {
var screenWriter *terminal.ScreenWriter
if !invocation.HasChunkedEventLogs {
screenWriter = terminal.NewScreenWriter()
}
var redactor *redact.StreamingRedactor
if ti.RedactionFlags&redact.RedactionFlagStandardRedactions != redact.RedactionFlagStandardRedactions {
// only redact if we hadn't redacted enough, only parse again if we redact
redactor = redact.NewStreamingRedactor(env)
}
beValues := accumulator.NewBEValues(invocation)
events := []*inpb.InvocationEvent{}
structuredCommandLines := []*command_line.CommandLine{}
err := streamRawInvocationEvents(env, ctx, streamID, func(event *inpb.InvocationEvent) error {
if redactor != nil {
if err := redactor.RedactAPIKeysWithSlowRegexp(ctx, event.BuildEvent); err != nil {
return err
}
if err := redactor.RedactMetadata(event.BuildEvent); err != nil {
return err
}
if err := beValues.AddEvent(event.BuildEvent); err != nil {
return err
}
}
return LookupInvocationEventsWithCallback(ctx, env, invocation, ti.RedactionFlags, cb)
})

switch p := event.BuildEvent.Payload.(type) {
case *build_event_stream.BuildEvent_Started:
// Drop child pattern expanded events since this list can be
// very long and we don't render these currently.
event.BuildEvent.Children = nil
case *build_event_stream.BuildEvent_Expanded:
if len(event.BuildEvent.GetId().GetPattern().GetPattern()) > 0 {
pattern, truncated := TruncateStringSlice(event.BuildEvent.GetId().GetPattern().GetPattern(), maxPatternLengthBytes)
invocation.PatternsTruncated = truncated
event.BuildEvent.GetId().GetPattern().Pattern = pattern
}
// Don't return child TargetConfigured events to the UI; the UI
// only cares about the actual TargetConfigured event payloads.
event.BuildEvent.Children = nil
// UI doesn't render TestSuiteExpansions yet (though we probably
// should at some point?) So don't return these either.
p.Expanded.TestSuiteExpansions = nil
case *build_event_stream.BuildEvent_Progress:
if screenWriter != nil {
screenWriter.Write([]byte(p.Progress.Stderr))
screenWriter.Write([]byte(p.Progress.Stdout))
}
// Don't serve progress event contents to the UI since they are too
// large. Instead, logs are available either via the
// console_buffer field or the separate logs RPC.
p.Progress.Stderr = ""
p.Progress.Stdout = ""
case *build_event_stream.BuildEvent_StructuredCommandLine:
structuredCommandLines = append(structuredCommandLines, p.StructuredCommandLine)
}
if err := eg.Wait(); err != nil {
return nil, err
}

if err := cb(event); err != nil {
invocation.ScoreCard = scoreCard
return invocation, nil
}

func LookupInvocationEventsWithCallback(ctx context.Context, env environment.Env, inv *inpb.Invocation, invRedactionFlags int32, cb invocationEventCB) error {
var screenWriter *terminal.ScreenWriter
if !inv.HasChunkedEventLogs {
screenWriter = terminal.NewScreenWriter()
}
var redactor *redact.StreamingRedactor
if invRedactionFlags&redact.RedactionFlagStandardRedactions != redact.RedactionFlagStandardRedactions {
// only redact if we hadn't redacted enough, only parse again if we redact
redactor = redact.NewStreamingRedactor(env)
}
beValues := accumulator.NewBEValues(inv)
structuredCommandLines := []*command_line.CommandLine{}
streamID := GetStreamIdFromInvocationIdAndAttempt(inv.GetInvocationId(), inv.GetAttempt())
err := streamRawInvocationEvents(env, ctx, streamID, func(event *inpb.InvocationEvent) error {
if redactor != nil {
if err := redactor.RedactAPIKeysWithSlowRegexp(ctx, event.BuildEvent); err != nil {
return err
}
return nil
})
if err != nil {
return err
if err := redactor.RedactMetadata(event.BuildEvent); err != nil {
return err
}
if err := beValues.AddEvent(event.BuildEvent); err != nil {
return err
}
}

switch p := event.BuildEvent.Payload.(type) {
case *build_event_stream.BuildEvent_Started:
// Drop child pattern expanded events since this list can be
// very long and we don't render these currently.
event.BuildEvent.Children = nil
case *build_event_stream.BuildEvent_Expanded:
if len(event.BuildEvent.GetId().GetPattern().GetPattern()) > 0 {
pattern, truncated := TruncateStringSlice(event.BuildEvent.GetId().GetPattern().GetPattern(), maxPatternLengthBytes)
inv.PatternsTruncated = truncated
event.BuildEvent.GetId().GetPattern().Pattern = pattern
}
// Don't return child TargetConfigured events to the UI; the UI
// only cares about the actual TargetConfigured event payloads.
event.BuildEvent.Children = nil
// UI doesn't render TestSuiteExpansions yet (though we probably
// should at some point?) So don't return these either.
p.Expanded.TestSuiteExpansions = nil
case *build_event_stream.BuildEvent_Progress:
if screenWriter != nil {
screenWriter.Write([]byte(p.Progress.Stderr))
screenWriter.Write([]byte(p.Progress.Stdout))
}
// Don't serve progress event contents to the UI since they are too
// large. Instead, logs are available either via the
// console_buffer field or the separate logs RPC.
p.Progress.Stderr = ""
p.Progress.Stdout = ""
case *build_event_stream.BuildEvent_StructuredCommandLine:
structuredCommandLines = append(structuredCommandLines, p.StructuredCommandLine)
}

invocation.Event = events
// TODO: Can we remove this StructuredCommandLine field? These are
// already available in the events list.
invocation.StructuredCommandLine = structuredCommandLines
if screenWriter != nil {
invocation.ConsoleBuffer = string(screenWriter.Render())
if err := cb(event); err != nil {
return err
}
return nil
})

if err := eg.Wait(); err != nil {
return nil, err
if err != nil {
return err
}

invocation.ScoreCard = scoreCard
return invocation, nil
// TODO: Can we remove this StructuredCommandLine field? These are
// already available in the events list.
inv.StructuredCommandLine = structuredCommandLines
if screenWriter != nil {
inv.ConsoleBuffer = string(screenWriter.Render())
}
return nil
}

func (e *EventChannel) tableInvocationFromProto(p *inpb.Invocation, blobID string) (*tables.Invocation, error) {
Expand Down
2 changes: 1 addition & 1 deletion server/buildbuddy_server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ go_library(
"//proto:workspace_go_proto",
"//proto:zip_go_proto",
"//server/backends/chunkstore",
"//server/backends/invocationdb",
"//server/build_event_protocol/build_event_handler",
"//server/build_event_protocol/event_index",
"//server/capabilities_filter",
Expand Down Expand Up @@ -65,6 +64,7 @@ go_library(
"//server/util/subdomain",
"@org_golang_google_grpc//metadata",
"@org_golang_google_protobuf//encoding/protojson",
"@org_golang_x_sync//errgroup",
"@org_golang_x_time//rate",
],
)
Expand Down
Loading
Loading