diff --git a/cmd/joj3/conf.go b/cmd/joj3/conf.go index 769b324..bcf28d8 100644 --- a/cmd/joj3/conf.go +++ b/cmd/joj3/conf.go @@ -1,32 +1,45 @@ package main import ( + "fmt" "log/slog" + "regexp" + "strings" "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" "github.com/go-git/go-git/v5" "github.com/koding/multiconfig" ) +type JobType int + +const ( + HC JobType = iota + CQ + OJ +) + +type Stage struct { + Name string + Executor struct { + Name string + With struct { + Default stage.Cmd + Cases []OptionalCmd + } + } + Parser struct { + Name string + With interface{} + } +} + type Conf struct { SandboxExecServer string `default:"localhost:5051"` SandboxToken string `default:""` LogLevel int `default:"0"` OutputPath string `default:"joj3_result.json"` - Stages []struct { - Name string - Executor struct { - Name string - With struct { - Default stage.Cmd - Cases []OptionalCmd - } - } - Parser struct { - Name string - With interface{} - } - } + Stages []Stage } type OptionalCmd struct { @@ -60,40 +73,113 @@ type OptionalCmd struct { AddressSpaceLimit *bool } -func parseConfFile(path string) (conf Conf, err error) { +func parseConfFile(path string, jobtype JobType) (conf Conf, err error) { d := &multiconfig.DefaultLoader{} d.Loader = multiconfig.MultiLoader( &multiconfig.TagLoader{}, &multiconfig.JSONLoader{Path: path}, ) d.Validator = multiconfig.MultiValidator(&multiconfig.RequiredValidator{}) + if err = d.Load(&conf); err != nil { slog.Error("parse stages conf", "error", err) return } + if err = d.Validate(&conf); err != nil { slog.Error("validate stages conf", "error", err) return } + + filteredStages := []Stage{} + for _, stage := range conf.Stages { + if filterStage(stage, jobtype) { + filteredStages = append(filteredStages, stage) + } + } + + conf.Stages = filteredStages + return } +func filterStage(stage Stage, jobtype JobType) bool { + switch jobtype { + case HC: + return stage.Name == "healthcheck" + case CQ: + return stage.Name == "compile" || stage.Name == "healthcheck" + case OJ: + return true + default: + return false + } +} + +func validateHw(hw string) error { + matched, err := regexp.MatchString(`^hw[0-9]+$`, hw) + if err != nil { + return fmt.Errorf("error compiling regex: %w", err) + } + if !matched { + return fmt.Errorf("error: hw does not match the required pattern") + } + return nil +} + func commitMsgToConf() (conf Conf, err error) { r, err := git.PlainOpen(".") if err != nil { return } + ref, err := r.Head() if err != nil { return } + commit, err := r.CommitObject(ref.Hash()) if err != nil { return } + + file := "conf.json" + jobtype := HC + msg := commit.Message slog.Debug("commit msg to conf", "msg", msg) - // TODO: parse msg to conf name - conf, err = parseConfFile("conf.json") + if msg == "" { + conf, err = parseConfFile(file, jobtype) + return + } + + line := strings.Split(msg, "\n")[0] + + words := strings.Fields(line) + + head := words[0] + var hw string + + if strings.HasSuffix(head, ":") || strings.HasSuffix(head, ".") { + head = head[:len(head)-1] + } + + if len(words) == 3 { + hw = words[1] + if err = validateHw(hw); err != nil { + return + } + + switch head { + case "feat", "fix", "refactor", "perf", "test", "build", "revert": + file = strings.Replace(file, "conf", "conf-"+hw, 1) + jobtype = CQ + case "joj", "grading": + file = strings.Replace(file, "conf", "conf-"+hw, 1) + jobtype = OJ + } + } + + conf, err = parseConfFile(file, jobtype) return }