Add runtime SQLite query targets
This commit is contained in:
+40
-13
@@ -67,6 +67,7 @@ type LifecycleExecutor struct {
|
||||
localStartupDiagnostics bool
|
||||
protectedRequests *ProtectedRequestRegistry
|
||||
sqliteSchemaProbe *SQLiteSchemaProbeExecutor
|
||||
sqliteQuery *SQLiteQueryExecutor
|
||||
metricCollector MetricCollector
|
||||
}
|
||||
|
||||
@@ -118,6 +119,7 @@ func NewLifecycleExecutor(options ...LifecycleExecutorOption) LifecycleExecutor
|
||||
executor.fileExecutor = files
|
||||
}
|
||||
executor.sqliteSchemaProbe = NewSQLiteSchemaProbeExecutor(executor.workspaceRoot)
|
||||
executor.sqliteQuery = NewSQLiteQueryExecutor(executor.workspaceRoot)
|
||||
return executor
|
||||
}
|
||||
|
||||
@@ -389,6 +391,9 @@ func (executor LifecycleExecutor) ExecuteContext(ctx context.Context, assignment
|
||||
return lifecycleFailure("unsafe_lifecycle_command", err.Error())
|
||||
}
|
||||
log.Printf("RUN phase=lifecycle.command status=starting job=%s workdir=%s command=%s timeoutMs=%d envKeys=%s", assignment.JobID, safeOptional(command.WorkDir), redactedCommandLine(command.Args), command.Timeout.Milliseconds(), envKeysSummary(command.Env, nil))
|
||||
command.OutputLine = func(stream string, line string) {
|
||||
_ = executor.logSink.Append(ctx, assignment, stream, line)
|
||||
}
|
||||
result, err := executor.supervisor.Run(ctx, command)
|
||||
if err != nil && ctx.Err() != nil {
|
||||
log.Printf("RUN phase=lifecycle.command status=cancelled job=%s error=%s", assignment.JobID, RedactText(ctx.Err().Error()))
|
||||
@@ -399,7 +404,9 @@ func (executor LifecycleExecutor) ExecuteContext(ctx context.Context, assignment
|
||||
ErrorCode: "lifecycle_cancelled",
|
||||
}
|
||||
}
|
||||
executor.writeProcessLogs(ctx, assignment, result)
|
||||
if !result.OutputRelayed {
|
||||
executor.writeProcessLogs(ctx, assignment, result)
|
||||
}
|
||||
if err != nil {
|
||||
log.Printf("RUN phase=lifecycle.command status=failed job=%s exitCode=%d error=%s", assignment.JobID, result.ExitCode, RedactText(err.Error()))
|
||||
return lifecycleFailure("lifecycle_process_failed", RedactText(err.Error()))
|
||||
@@ -519,7 +526,7 @@ func (executor LifecycleExecutor) writeProcessLogs(ctx context.Context, assignme
|
||||
{stream: "stdout", body: result.Stdout},
|
||||
{stream: "stderr", body: result.Stderr},
|
||||
} {
|
||||
for _, line := range splitBoundedLines(item.body) {
|
||||
for _, line := range splitRawLogLines(item.body) {
|
||||
_ = executor.logSink.Append(ctx, assignment, item.stream, line)
|
||||
}
|
||||
}
|
||||
@@ -919,12 +926,14 @@ type ProcessCommand struct {
|
||||
JobID string
|
||||
Capability string
|
||||
Action string
|
||||
OutputLine func(string, string)
|
||||
}
|
||||
|
||||
type ProcessResult struct {
|
||||
ExitCode int
|
||||
Stdout string
|
||||
Stderr string
|
||||
ExitCode int
|
||||
Stdout string
|
||||
Stderr string
|
||||
OutputRelayed bool
|
||||
}
|
||||
|
||||
type ProcessSupervisor interface {
|
||||
@@ -969,7 +978,7 @@ func (supervisor OSProcessSupervisor) Run(ctx context.Context, command ProcessCo
|
||||
err := cmd.Wait()
|
||||
stdoutWriter.Flush()
|
||||
stderrWriter.Flush()
|
||||
result := ProcessResult{Stdout: RedactText(stdout.String()), Stderr: RedactText(stderr.String())}
|
||||
result := ProcessResult{Stdout: stdout.String(), Stderr: stderr.String(), OutputRelayed: command.OutputLine != nil}
|
||||
if cmd.ProcessState != nil {
|
||||
result.ExitCode = cmd.ProcessState.ExitCode()
|
||||
}
|
||||
@@ -1011,7 +1020,7 @@ func (writer *lifecycleOutputWriter) Write(p []byte) (int, error) {
|
||||
if index < 0 {
|
||||
break
|
||||
}
|
||||
line := strings.TrimRight(writer.pending[:index], "\r")
|
||||
line := writer.pending[:index]
|
||||
writer.pending = writer.pending[index+1:]
|
||||
writer.logLine(line)
|
||||
}
|
||||
@@ -1021,19 +1030,17 @@ func (writer *lifecycleOutputWriter) Write(p []byte) (int, error) {
|
||||
func (writer *lifecycleOutputWriter) Flush() {
|
||||
writer.mu.Lock()
|
||||
defer writer.mu.Unlock()
|
||||
if strings.TrimSpace(writer.pending) == "" {
|
||||
writer.pending = ""
|
||||
if writer.pending == "" {
|
||||
return
|
||||
}
|
||||
writer.logLine(strings.TrimRight(writer.pending, "\r"))
|
||||
writer.logLine(writer.pending)
|
||||
writer.pending = ""
|
||||
}
|
||||
|
||||
func (writer *lifecycleOutputWriter) logLine(line string) {
|
||||
if strings.TrimSpace(line) == "" {
|
||||
return
|
||||
if writer.command.OutputLine != nil {
|
||||
writer.command.OutputLine(writer.stream, line)
|
||||
}
|
||||
log.Printf("RUN phase=process.command.output status=line job=%s capability=%s action=%s stream=%s line=%q", safeOptional(writer.command.JobID), safeOptional(writer.command.Capability), safeOptional(writer.command.Action), writer.stream, RedactText(line))
|
||||
}
|
||||
|
||||
type ioLimitWriter struct {
|
||||
@@ -1242,6 +1249,26 @@ func splitBoundedLines(value string) []string {
|
||||
return out
|
||||
}
|
||||
|
||||
// splitRawLogLines only removes the newline framing used by LogEntry. It
|
||||
// deliberately preserves every other byte, including blank lines and spaces.
|
||||
func splitRawLogLines(value string) []string {
|
||||
if value == "" {
|
||||
return nil
|
||||
}
|
||||
lines := make([]string, 0, strings.Count(value, "\n")+1)
|
||||
for start := 0; start < len(value); {
|
||||
end := strings.IndexByte(value[start:], '\n')
|
||||
if end < 0 {
|
||||
lines = append(lines, value[start:])
|
||||
break
|
||||
}
|
||||
end += start
|
||||
lines = append(lines, value[start:end])
|
||||
start = end + 1
|
||||
}
|
||||
return lines
|
||||
}
|
||||
|
||||
func checksumForText(value string) string {
|
||||
sum := sha256.Sum256([]byte(value))
|
||||
return "sha256:" + hex.EncodeToString(sum[:])
|
||||
|
||||
Reference in New Issue
Block a user