From aa0b33d1e7e6ab16e06d6438531e4a8884c433ca Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Mon, 25 Nov 2024 05:03:42 -0500 Subject: [PATCH] feat(cmd/joj3): check with teapot before stages --- cmd/joj3/conf/conf.go | 9 ++++++- cmd/joj3/main.go | 8 ++++++ cmd/joj3/teapot/check.go | 43 ++++++++++++++++++++++++++++++ cmd/joj3/teapot/exec.go | 57 ++++++++++++++++++++++++++++++++++++++++ cmd/joj3/teapot/run.go | 48 +++------------------------------ 5 files changed, 120 insertions(+), 45 deletions(-) create mode 100644 cmd/joj3/teapot/check.go create mode 100644 cmd/joj3/teapot/exec.go diff --git a/cmd/joj3/conf/conf.go b/cmd/joj3/conf/conf.go index 24450ca..7c3af10 100644 --- a/cmd/joj3/conf/conf.go +++ b/cmd/joj3/conf/conf.go @@ -35,6 +35,12 @@ type ConfStage struct { } } +type ConfGroup struct { + Name string + MaxCount int + TimePeriodHour int +} + type Conf struct { Name string `default:"unknown"` LogPath string `default:""` @@ -57,7 +63,8 @@ type Conf struct { SkipScoreboard bool `default:"false"` SkipFailedTable bool `default:"false"` SubmitterInIssueTitle bool `default:"true"` - MaxTotalScore int `default:"-1"` // TODO: remove me + Groups []ConfGroup + MaxTotalScore int `default:"-1"` // TODO: remove me } // TODO: remove the following backward compatibility fields SandboxExecServer string `default:"localhost:5051"` diff --git a/cmd/joj3/main.go b/cmd/joj3/main.go index 090a3ed..08b76e0 100644 --- a/cmd/joj3/main.go +++ b/cmd/joj3/main.go @@ -106,6 +106,14 @@ func mainImpl() (err error) { return err } groups := conf.MatchGroups(confObj, conventionalCommit) + if len(confObj.Teapot.Groups) != 0 { + if err = teapot.Check(confObj); err != nil { + slog.Error("teapot check", "error", err) + return err + } + } else { + slog.Info("teapot check disabled") + } stageResults, forceQuitStageName, err = stage.Run(confObj, groups) if err != nil { slog.Error("stage run", "error", err) diff --git a/cmd/joj3/teapot/check.go b/cmd/joj3/teapot/check.go new file mode 100644 index 0000000..9efbc13 --- /dev/null +++ b/cmd/joj3/teapot/check.go @@ -0,0 +1,43 @@ +package teapot + +import ( + "fmt" + "log/slog" + "os" + "strings" + + "github.com/joint-online-judge/JOJ3/cmd/joj3/conf" + "github.com/joint-online-judge/JOJ3/cmd/joj3/env" +) + +func Check(conf *conf.Conf) (err error) { + os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath) + os.Setenv("_TYPER_STANDARD_TRACEBACK", "1") + if env.Attr.Actor == "" || + env.Attr.Repository == "" || + strings.Count(env.Attr.Repository, "/") != 1 { + slog.Error("teapot env not set") + err = fmt.Errorf("teapot env not set") + return + } + repoParts := strings.Split(env.Attr.Repository, "/") + repoName := repoParts[1] + var formattedGroups []string + for _, group := range conf.Teapot.Groups { + groupConfig := fmt.Sprintf("%s=%d:%d", + group.Name, group.MaxCount, group.TimePeriodHour) + formattedGroups = append(formattedGroups, groupConfig) + } + args := []string{ + "joj3-check", conf.Teapot.EnvFilePath, + env.Attr.Actor, conf.Teapot.GradingRepoName, repoName, + conf.Teapot.ScoreboardPath, conf.Name, + "--group-config", strings.Join(formattedGroups, ","), + } + _, err = runCommand(args) + if err != nil { + slog.Error("teapot check exec", "error", err) + return + } + return +} diff --git a/cmd/joj3/teapot/exec.go b/cmd/joj3/teapot/exec.go new file mode 100644 index 0000000..c89d71d --- /dev/null +++ b/cmd/joj3/teapot/exec.go @@ -0,0 +1,57 @@ +package teapot + +import ( + "bufio" + "bytes" + "log/slog" + "os/exec" + "regexp" + "sync" +) + +func runCommand(args []string) ( + stdoutBuf *bytes.Buffer, err error, +) { + re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`) + cmd := exec.Command("joint-teapot", args...) // #nosec G204 + stdoutBuf = new(bytes.Buffer) + cmd.Stdout = stdoutBuf + stderr, err := cmd.StderrPipe() + if err != nil { + slog.Error("stderr pipe", "error", err) + return + } + 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 + } + return +} diff --git a/cmd/joj3/teapot/run.go b/cmd/joj3/teapot/run.go index dc1dfcc..dadadf2 100644 --- a/cmd/joj3/teapot/run.go +++ b/cmd/joj3/teapot/run.go @@ -1,17 +1,12 @@ package teapot import ( - "bufio" - "bytes" "encoding/json" "fmt" "log/slog" "os" - "os/exec" - "regexp" "strconv" "strings" - "sync" "github.com/joint-online-judge/JOJ3/cmd/joj3/conf" "github.com/joint-online-judge/JOJ3/cmd/joj3/env" @@ -54,8 +49,7 @@ func Run(conf *conf.Conf, groups []string) ( if conf.Teapot.SubmitterInIssueTitle { submitterInIssueTitleArg = "--submitter-in-issue-title" } - re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`) - cmd := exec.Command("joint-teapot", + args := []string{ "joj3-all", conf.Teapot.EnvFilePath, conf.Stage.OutputPath, env.Attr.Actor, conf.Teapot.GradingRepoName, repoName, env.Attr.RunNumber, conf.Teapot.ScoreboardPath, @@ -65,44 +59,10 @@ func Run(conf *conf.Conf, groups []string) ( "--max-total-score", strconv.Itoa(conf.MaxTotalScore), skipIssueArg, skipScoreboardArg, skipFailedTableArg, submitterInIssueTitleArg, - ) // #nosec G204 - stdoutBuf := new(bytes.Buffer) - cmd.Stdout = stdoutBuf - stderr, err := cmd.StderrPipe() + } + stdoutBuf, err := runCommand(args) if err != nil { - slog.Error("stderr pipe", "error", err) - return - } - 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) - } + slog.Error("teapot run exec", "error", err) return } if json.Unmarshal(stdoutBuf.Bytes(), &teapotResult) != nil {