c36aa18c67
Nuevas skills para crear TUIs, inicializar frontends React y módulos Go. Incluye binario parallel-executor y utilidades de soporte. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
136 lines
3.4 KiB
Go
136 lines
3.4 KiB
Go
package shell
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/lucasdataproyects/devfactory/core"
|
|
dfshell "github.com/lucasdataproyects/devfactory/shell"
|
|
|
|
pcore "github.com/lucasdataproyects/parallel-executor/core"
|
|
)
|
|
|
|
// Logger escribe logs de la ejecución paralela a disco.
|
|
type Logger struct {
|
|
logsDir string
|
|
sessionID string
|
|
}
|
|
|
|
// NewLogger crea un logger para la sesión actual.
|
|
func NewLogger(repoRoot string) core.Result[*Logger] {
|
|
sessionID := time.Now().Format("20060102-150405")
|
|
logsDir := repoRoot + "/logs"
|
|
|
|
result := dfshell.MkdirAll(logsDir)
|
|
if result.IsErr() {
|
|
return core.Err[*Logger](result.Error())
|
|
}
|
|
|
|
return core.Ok(&Logger{
|
|
logsDir: logsDir,
|
|
sessionID: sessionID,
|
|
})
|
|
}
|
|
|
|
// LogIssueStart registra el inicio de ejecución de una issue.
|
|
func (l *Logger) LogIssueStart(issue pcore.Issue) {
|
|
msg := fmt.Sprintf("[%s] START Issue #%04d - %s\n",
|
|
time.Now().Format("15:04:05"), issue.Number, issue.Title)
|
|
l.appendToSession(msg)
|
|
}
|
|
|
|
// LogIssueResult registra el resultado de una issue.
|
|
func (l *Logger) LogIssueResult(result pcore.ExecutionResult) {
|
|
status := "SUCCESS"
|
|
if !result.Success {
|
|
status = "FAILED"
|
|
}
|
|
|
|
msg := fmt.Sprintf("[%s] %s Issue #%04d - %s (duration: %s)",
|
|
time.Now().Format("15:04:05"), status,
|
|
result.Issue.Number, result.Issue.Title, result.Duration)
|
|
|
|
if result.Error != "" {
|
|
msg += "\n Error: " + result.Error
|
|
}
|
|
msg += "\n"
|
|
|
|
l.appendToSession(msg)
|
|
|
|
// Log individual por issue
|
|
issueLogFile := fmt.Sprintf("%s/issue-%04d-%s.log",
|
|
l.logsDir, result.Issue.Number, l.sessionID)
|
|
content := fmt.Sprintf("Issue #%04d - %s\nStatus: %s\nDuration: %s\n",
|
|
result.Issue.Number, result.Issue.Title, status, result.Duration)
|
|
if result.Error != "" {
|
|
content += "Error:\n" + result.Error + "\n"
|
|
}
|
|
dfshell.WriteString(issueLogFile, content)
|
|
}
|
|
|
|
// WriteSummary escribe el resumen consolidado.
|
|
func (l *Logger) WriteSummary(results []pcore.ExecutionResult) core.Result[string] {
|
|
summaryFile := fmt.Sprintf("%s/summary-%s.txt", l.logsDir, l.sessionID)
|
|
|
|
var b strings.Builder
|
|
b.WriteString("=" + strings.Repeat("=", 59) + "\n")
|
|
b.WriteString(fmt.Sprintf(" Parallel Execution Summary — %s\n", l.sessionID))
|
|
b.WriteString("=" + strings.Repeat("=", 59) + "\n\n")
|
|
|
|
succeeded := 0
|
|
failed := 0
|
|
for _, r := range results {
|
|
if r.Success {
|
|
succeeded++
|
|
} else {
|
|
failed++
|
|
}
|
|
}
|
|
|
|
b.WriteString(fmt.Sprintf("Total: %d\n", len(results)))
|
|
b.WriteString(fmt.Sprintf("Succeeded: %d\n", succeeded))
|
|
b.WriteString(fmt.Sprintf("Failed: %d\n\n", failed))
|
|
|
|
b.WriteString("Results:\n")
|
|
b.WriteString(strings.Repeat("-", 60) + "\n")
|
|
|
|
for _, r := range results {
|
|
status := "✓"
|
|
if !r.Success {
|
|
status = "✗"
|
|
}
|
|
b.WriteString(fmt.Sprintf(" %s #%04d %-30s %s\n",
|
|
status, r.Issue.Number, r.Issue.Title, r.Duration))
|
|
if r.Error != "" {
|
|
b.WriteString(fmt.Sprintf(" Error: %s\n", truncate(r.Error, 80)))
|
|
}
|
|
}
|
|
|
|
b.WriteString(strings.Repeat("-", 60) + "\n")
|
|
|
|
writeResult := dfshell.WriteString(summaryFile, b.String())
|
|
if writeResult.IsErr() {
|
|
return core.Err[string](writeResult.Error())
|
|
}
|
|
|
|
return core.Ok(summaryFile)
|
|
}
|
|
|
|
// SessionLogFile retorna la ruta del log de sesión.
|
|
func (l *Logger) SessionLogFile() string {
|
|
return fmt.Sprintf("%s/parallel-execution-%s.log", l.logsDir, l.sessionID)
|
|
}
|
|
|
|
func (l *Logger) appendToSession(msg string) {
|
|
logFile := l.SessionLogFile()
|
|
dfshell.AppendFile(logFile, []byte(msg))
|
|
}
|
|
|
|
func truncate(s string, max int) string {
|
|
if len(s) <= max {
|
|
return s
|
|
}
|
|
return s[:max-3] + "..."
|
|
}
|