From e4bfcfbe8dfe9991314722fe0d8b94b22b9ef263 Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sun, 10 Nov 2024 22:57:45 -0500 Subject: [PATCH] feat(cmd/joj3): extra summary info from teapot [force build] --- cmd/joj3/main.go | 41 ++++++++++++-- cmd/joj3/stage/run.go | 2 + cmd/joj3/stage/summarize.go | 34 ------------ cmd/joj3/teapot/run.go | 108 ++++++++++++++++++++---------------- 4 files changed, 97 insertions(+), 88 deletions(-) delete mode 100644 cmd/joj3/stage/summarize.go diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go index d387308..1c2f8f6 100644 --- a/cmd/joj3/main.go +++ b/cmd/joj3/main.go @@ -9,6 +9,7 @@ import ( "github.com/joint-online-judge/JOJ3/cmd/joj3/conf" "github.com/joint-online-judge/JOJ3/cmd/joj3/stage" "github.com/joint-online-judge/JOJ3/cmd/joj3/teapot" + internalStage "github.com/joint-online-judge/JOJ3/internal/stage" ) var ( @@ -31,7 +32,37 @@ func init() { showVersion = flag.Bool("version", false, "print current version") } -func mainImpl() error { +func mainImpl() (err error) { + confObj := new(conf.Conf) + var stageResults []internalStage.StageResult + var stageForceQuit bool + var teapotResult teapot.TeapotResult + defer func() { + actor := os.Getenv("GITHUB_ACTOR") + repository := os.Getenv("GITHUB_REPOSITORY") + ref := os.Getenv("GITHUB_REF") + workflow := os.Getenv("GITHUB_WORKFLOW") + totalScore := 0 + for _, stageResult := range stageResults { + for _, result := range stageResult.Results { + totalScore += result.Score + } + } + slog.Info( + "joj3 summary", + "name", confObj.Name, + "totalScore", totalScore, + "forceQuit", stageForceQuit, + "actor", actor, + "repository", repository, + "ref", ref, + "workflow", workflow, + "issue", teapotResult.Issue, + "action", teapotResult.Action, + "sha", teapotResult.Sha, + "error", err, + ) + }() if err := setupSlog(""); err != nil { // before conf is loaded slog.Error("setup slog", "error", err) return err @@ -57,7 +88,7 @@ func mainImpl() error { return err } slog.Info("try to load conf", "path", confPath) - confObj, err := conf.ParseConfFile(confPath) + confObj, err = conf.ParseConfFile(confPath) if err != nil { slog.Error("parse conf", "error", err) return err @@ -79,16 +110,16 @@ func mainImpl() error { return err } groups := conf.MatchGroups(confObj, conventionalCommit) - stageResults, stageForceQuit, err := stage.Run(confObj, groups) + stageResults, stageForceQuit, err = stage.Run(confObj, groups) if err != nil { slog.Error("stage run", "error", err) } - stage.Summarize(confObj, stageResults, stageForceQuit) if err = stage.Write(confObj.Stage.OutputPath, stageResults); err != nil { slog.Error("stage write", "error", err) return err } - if err := teapot.Run(confObj, runID); err != nil { + teapotResult, err = teapot.Run(confObj, runID) + if err != nil { slog.Error("teapot run", "error", err) return err } diff --git a/cmd/joj3/stage/run.go b/cmd/joj3/stage/run.go index 6281034..e07a167 100644 --- a/cmd/joj3/stage/run.go +++ b/cmd/joj3/stage/run.go @@ -13,6 +13,8 @@ import ( "github.com/jinzhu/copier" ) +type StageResult stage.StageResult + func generateStages(conf *conf.Conf, groups []string) ([]stage.Stage, error) { stages := []stage.Stage{} existNames := map[string]bool{} diff --git a/cmd/joj3/stage/summarize.go b/cmd/joj3/stage/summarize.go deleted file mode 100644 index 0c626f4..0000000 --- a/cmd/joj3/stage/summarize.go +++ /dev/null @@ -1,34 +0,0 @@ -package stage - -import ( - "log/slog" - "os" - - "github.com/joint-online-judge/JOJ3/cmd/joj3/conf" - "github.com/joint-online-judge/JOJ3/internal/stage" -) - -func Summarize( - conf *conf.Conf, stageResults []stage.StageResult, stageForceQuit bool, -) { - actor := os.Getenv("GITHUB_ACTOR") - repository := os.Getenv("GITHUB_REPOSITORY") - ref := os.Getenv("GITHUB_REF") - workflow := os.Getenv("GITHUB_WORKFLOW") - totalScore := 0 - for _, stageResult := range stageResults { - for _, result := range stageResult.Results { - totalScore += result.Score - } - } - slog.Info( - "stage summary", - "name", conf.Name, - "totalScore", totalScore, - "forceQuit", stageForceQuit, - "actor", actor, - "repository", repository, - "ref", ref, - "workflow", workflow, - ) -} diff --git a/cmd/joj3/teapot/run.go b/cmd/joj3/teapot/run.go index fbafae6..e449382 100644 --- a/cmd/joj3/teapot/run.go +++ b/cmd/joj3/teapot/run.go @@ -2,6 +2,8 @@ package teapot import ( "bufio" + "bytes" + "encoding/json" "fmt" "log/slog" "os" @@ -14,7 +16,13 @@ import ( "github.com/joint-online-judge/JOJ3/cmd/joj3/conf" ) -func Run(conf *conf.Conf, runID string) error { +type TeapotResult struct { + Issue int `json:"issue"` + Action int `json:"action"` + Sha string `json:"sha"` +} + +func Run(conf *conf.Conf, runID string) (teapotResult TeapotResult, err error) { os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath) os.Setenv("_TYPER_STANDARD_TRACEBACK", "1") sha := os.Getenv("GITHUB_SHA") @@ -24,52 +32,11 @@ func Run(conf *conf.Conf, runID string) error { if actor == "" || repository == "" || strings.Count(repository, "/") != 1 || runNumber == "" { slog.Error("teapot env not set") - return fmt.Errorf("teapot env not set") + err = fmt.Errorf("teapot env not set") + return } repoParts := strings.Split(repository, "/") repoName := repoParts[1] - re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`) - execCommand := func(name string, cmdArgs []string) error { - cmd := exec.Command(name, cmdArgs...) // #nosec G204 - stderr, err := cmd.StderrPipe() - if err != nil { - slog.Error("stderr pipe", "error", err) - return err - } - var wg sync.WaitGroup - wg.Add(1) - scanner := bufio.NewScanner(stderr) - go func() { - for scanner.Scan() { - text := re.ReplaceAllString(scanner.Text(), "") - if text == "" { - continue - } - slog.Info(name, "stderr", text) - } - wg.Done() - if scanner.Err() != nil { - slog.Error("stderr scanner", "error", scanner.Err()) - } - }() - if err = cmd.Start(); err != nil { - slog.Error("cmd start", "error", err) - return err - } - wg.Wait() - if err = cmd.Wait(); err != nil { - if exitErr, ok := err.(*exec.ExitError); ok { - exitCode := exitErr.ExitCode() - slog.Error("cmd completed with non-zero exit code", - "error", err, - "exitCode", exitCode) - } else { - slog.Error("cmd wait", "error", err) - } - return err - } - return nil - } skipIssueArg := "--no-skip-result-issue" if conf.Teapot.SkipIssue { skipIssueArg = "--skip-result-issue" @@ -86,7 +53,8 @@ func Run(conf *conf.Conf, runID string) error { if conf.Teapot.SubmitterInIssueTitle { submitterInIssueTitleArg = "--submitter-in-issue-title" } - if err := execCommand("joint-teapot", []string{ + re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`) + cmd := exec.Command("joint-teapot", "joj3-all", conf.Teapot.EnvFilePath, conf.Stage.OutputPath, actor, conf.Teapot.GradingRepoName, repoName, runNumber, conf.Teapot.ScoreboardPath, conf.Teapot.FailedTablePath, @@ -94,9 +62,51 @@ func Run(conf *conf.Conf, runID string) error { "--max-total-score", strconv.Itoa(conf.Teapot.MaxTotalScore), skipIssueArg, skipScoreboardArg, skipFailedTableArg, submitterInIssueTitleArg, - }); err != nil { - slog.Error("teapot exit", "error", err) - return fmt.Errorf("teapot exit") + ) // #nosec G204 + stdoutBuf := new(bytes.Buffer) + cmd.Stdout = stdoutBuf + stderr, err := cmd.StderrPipe() + if err != nil { + slog.Error("stderr pipe", "error", err) + return } - return nil + var wg sync.WaitGroup + wg.Add(1) + scanner := bufio.NewScanner(stderr) + go func() { + for scanner.Scan() { + text := re.ReplaceAllString(scanner.Text(), "") + if text == "" { + continue + } + slog.Info("joint-teapot", "stderr", text) + } + wg.Done() + if scanner.Err() != nil { + slog.Error("stderr scanner", "error", scanner.Err()) + } + }() + if err = cmd.Start(); err != nil { + slog.Error("cmd start", "error", err) + return + } + wg.Wait() + if err = cmd.Wait(); err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + exitCode := exitErr.ExitCode() + slog.Error("cmd completed with non-zero exit code", + "error", err, + "exitCode", exitCode) + } else { + slog.Error("cmd wait", "error", err) + } + return + } + if json.Unmarshal(stdoutBuf.Bytes(), &teapotResult) != nil { + slog.Error("unmarshal teapot result", "error", err, + "stdout", stdoutBuf.String()) + return + } + slog.Info("teapot result", "result", teapotResult) + return }