refactor(cmd/joj3)!: conf #51
							
								
								
									
										2
									
								
								Makefile
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								Makefile
									
									
									
									
									
								
							|  | @ -30,4 +30,4 @@ test: | |||
| ci-test: | ||||
| 	./scripts/prepare_test_repos.sh $(TMP_DIR) | ||||
| 	./scripts/run_foreach_test_repos.sh $(TMP_DIR) "sed -i '2i \ \ \"sandboxExecServer\": \"172.17.0.1:5051\",' conf.json" | ||||
| 	go test -coverprofile cover.out -v ./... | ||||
| 	GITHUB_ACTIONS="test" go test -coverprofile cover.out -v ./... | ||||
|  |  | |||
|  | @ -13,16 +13,48 @@ import ( | |||
| 	"github.com/koding/multiconfig" | ||||
| ) | ||||
| 
 | ||||
| type ConfStage struct { | ||||
| 	Name     string | ||||
| 	Group    string | ||||
| 	Executor struct { | ||||
| 		Name string | ||||
| 		With struct { | ||||
| 			Default stage.Cmd | ||||
| 			Cases   []OptionalCmd | ||||
| 		} | ||||
| 	} | ||||
| 	Parsers []struct { | ||||
| 		Name string | ||||
| 		With interface{} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type Conf struct { | ||||
| 	Name    string `default:"unknown"` | ||||
| 	LogPath string `default:""` | ||||
| 	Stage   struct { | ||||
| 		SandboxExecServer string `default:"localhost:5051"` | ||||
| 		SandboxToken      string `default:""` | ||||
| 		OutputPath        string `default:"joj3_result.json"` | ||||
| 		Stages            []ConfStage | ||||
| 	} | ||||
| 	Teapot struct { | ||||
| 		LogPath         string `default:"/home/tt/.cache/joint-teapot-debug.log"` | ||||
| 		ScoreboardPath  string `default:"scoreboard.csv"` | ||||
| 		FailedTablePath string `default:"failed-table.md"` | ||||
| 		GradingRepoName string `default:""` | ||||
| 		SkipIssue       bool   `default:"false"` | ||||
| 		SkipScoreboard  bool   `default:"false"` | ||||
| 		SkipFailedTable bool   `default:"false"` | ||||
| 	} | ||||
| 	// TODO: remove the following backward compatibility fields
 | ||||
| 	SandboxExecServer string `default:"localhost:5051"` | ||||
| 	SandboxToken      string `default:""` | ||||
| 	LogPath           string `default:""` | ||||
| 	OutputPath        string `default:"joj3_result.json"` | ||||
| 	GradingRepoName   string `default:""` | ||||
| 	SkipTeapot        bool   `default:"true"` | ||||
| 	ScoreboardPath    string `default:"scoreboard.csv"` | ||||
| 	FailedTablePath   string `default:"failed-table.md"` | ||||
| 	Name              string `default:"unknown"` | ||||
| 	Stages            []struct { | ||||
| 		Name     string | ||||
| 		Group    string | ||||
|  | @ -127,6 +159,27 @@ func parseConfFile(path string) (conf Conf, err error) { | |||
| 		slog.Error("validate stages conf", "error", err) | ||||
| 		return | ||||
| 	} | ||||
| 	// TODO: remove the following backward compatibility codes
 | ||||
| 	if len(conf.Stage.Stages) == 0 { | ||||
| 		conf.Stage.SandboxExecServer = conf.SandboxExecServer | ||||
| 		conf.Stage.SandboxToken = conf.SandboxToken | ||||
| 		conf.Stage.OutputPath = conf.OutputPath | ||||
| 		conf.Stage.Stages = make([]ConfStage, len(conf.Stages)) | ||||
| 		for i, stage := range conf.Stages { | ||||
| 			conf.Stage.Stages[i].Name = stage.Name | ||||
| 			conf.Stage.Stages[i].Group = stage.Group | ||||
| 			conf.Stage.Stages[i].Executor = stage.Executor | ||||
| 			conf.Stage.Stages[i].Parsers = []struct { | ||||
| 				Name string | ||||
| 				With interface{} | ||||
| 			}{ | ||||
| 				{ | ||||
| 					Name: stage.Parser.Name, | ||||
| 					With: stage.Parser.With, | ||||
| 				}, | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ import ( | |||
| func generateStages(conf conf.Conf, group string) ([]stage.Stage, error) { | ||||
| 	stages := []stage.Stage{} | ||||
| 	existNames := map[string]bool{} | ||||
| 	for _, s := range conf.Stages { | ||||
| 	for _, s := range conf.Stage.Stages { | ||||
| 		if s.Group != "" && group != s.Group { | ||||
| 			continue | ||||
| 		} | ||||
|  | @ -50,12 +50,20 @@ func generateStages(conf conf.Conf, group string) ([]stage.Stage, error) { | |||
| 		if len(s.Executor.With.Cases) == 0 { | ||||
| 			cmds = []stage.Cmd{defaultCmd} | ||||
| 		} | ||||
| 		parsers := []stage.StageParser{} | ||||
| 		for _, p := range s.Parsers { | ||||
| 			parsers = append(parsers, stage.StageParser{ | ||||
| 				Name: p.Name, | ||||
| 				Conf: p.With, | ||||
| 			}) | ||||
| 		} | ||||
| 		stages = append(stages, stage.Stage{ | ||||
| 			Name:         s.Name, | ||||
| 			ExecutorName: s.Executor.Name, | ||||
| 			ExecutorCmds: cmds, | ||||
| 			ParserName:   s.Parser.Name, | ||||
| 			ParserConf:   s.Parser.With, | ||||
| 			Name: s.Name, | ||||
| 			Executor: stage.StageExecutor{ | ||||
| 				Name: s.Executor.Name, | ||||
| 				Cmds: cmds, | ||||
| 			}, | ||||
| 			Parsers: parsers, | ||||
| 		}) | ||||
| 	} | ||||
| 	slog.Debug("stages generated", "stages", stages) | ||||
|  | @ -74,7 +82,10 @@ func outputResult(outputPath string, results []stage.StageResult) error { | |||
| } | ||||
| 
 | ||||
| func Run(conf conf.Conf, group string) error { | ||||
| 	executors.InitWithConf(conf.SandboxExecServer, conf.SandboxToken) | ||||
| 	executors.InitWithConf( | ||||
| 		conf.Stage.SandboxExecServer, | ||||
| 		conf.Stage.SandboxToken, | ||||
| 	) | ||||
| 	stages, err := generateStages(conf, group) | ||||
| 	if err != nil { | ||||
| 		slog.Error("generate stages", "error", err) | ||||
|  | @ -86,7 +97,7 @@ func Run(conf conf.Conf, group string) error { | |||
| 		slog.Error("run stages", "error", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	if err := outputResult(conf.OutputPath, results); err != nil { | ||||
| 	if err := outputResult(conf.Stage.OutputPath, results); err != nil { | ||||
| 		slog.Error("output result", "error", err) | ||||
| 		return err | ||||
| 	} | ||||
|  |  | |||
|  | @ -12,12 +12,16 @@ import ( | |||
| ) | ||||
| 
 | ||||
| func Run(conf conf.Conf) error { | ||||
| 	if conf.SkipTeapot { | ||||
| 	actions := os.Getenv("GITHUB_ACTIONS") | ||||
| 	if actions != "true" { | ||||
| 		slog.Info("teapot exit", "GITHUB_ACTIONS", actions) | ||||
| 		return nil | ||||
| 	} | ||||
| 	os.Setenv("LOG_FILE_PATH", "/home/tt/.cache/joint-teapot-debug.log") | ||||
| 	os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath) | ||||
| 	os.Setenv("_TYPER_STANDARD_TRACEBACK", "1") | ||||
| 	envFilePath := "/home/tt/.config/teapot/teapot.env" | ||||
| 	// TODO: pass sha to joint-teapot
 | ||||
| 	// sha := os.Getenv("GITHUB_SHA")
 | ||||
| 	actor := os.Getenv("GITHUB_ACTOR") | ||||
| 	repository := os.Getenv("GITHUB_REPOSITORY") | ||||
| 	runNumber := os.Getenv("GITHUB_RUN_NUMBER") | ||||
|  | @ -29,49 +33,55 @@ func Run(conf conf.Conf) error { | |||
| 	repoParts := strings.Split(repository, "/") | ||||
| 	repoName := repoParts[1] | ||||
| 	re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`) | ||||
| 	cmd := exec.Command("joint-teapot", "joj3-scoreboard", | ||||
| 		envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName, | ||||
| 		runNumber, conf.ScoreboardPath, conf.Name) // #nosec G204
 | ||||
| 	outputBytes, err := cmd.CombinedOutput() | ||||
| 	output := re.ReplaceAllString(string(outputBytes), "") | ||||
| 	for _, line := range strings.Split(output, "\n") { | ||||
| 		if line == "" { | ||||
| 			continue | ||||
| 	if !conf.Teapot.SkipScoreboard { | ||||
| 		cmd := exec.Command("joint-teapot", "joj3-scoreboard", | ||||
| 			envFilePath, conf.Stage.OutputPath, actor, conf.Teapot.GradingRepoName, | ||||
| 			repoName, runNumber, conf.Teapot.ScoreboardPath, conf.Name) // #nosec G204
 | ||||
| 		outputBytes, err := cmd.CombinedOutput() | ||||
| 		output := re.ReplaceAllString(string(outputBytes), "") | ||||
| 		for _, line := range strings.Split(output, "\n") { | ||||
| 			if line == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			slog.Info("joint-teapot joj3-scoreboard", "output", line) | ||||
| 		} | ||||
| 		slog.Info("joint-teapot joj3-scoreboard", "output", line) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		slog.Error("joint-teapot joj3-scoreboard", "err", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	cmd = exec.Command("joint-teapot", "joj3-failed-table", | ||||
| 		envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName, | ||||
| 		runNumber, conf.FailedTablePath, conf.Name) // #nosec G204
 | ||||
| 	outputBytes, err = cmd.CombinedOutput() | ||||
| 	output = re.ReplaceAllString(string(outputBytes), "") | ||||
| 	for _, line := range strings.Split(output, "\n") { | ||||
| 		if line == "" { | ||||
| 			continue | ||||
| 		if err != nil { | ||||
| 			slog.Error("joint-teapot joj3-scoreboard", "err", err) | ||||
| 			return err | ||||
| 		} | ||||
| 		slog.Info("joint-teapot joj3-failed-table", "output", line) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		slog.Error("joint-teapot joj3-failed-table", "err", err) | ||||
| 		return err | ||||
| 	} | ||||
| 	cmd = exec.Command("joint-teapot", "joj3-create-result-issue", | ||||
| 		envFilePath, conf.OutputPath, repoName, runNumber, conf.Name) // #nosec G204
 | ||||
| 	outputBytes, err = cmd.CombinedOutput() | ||||
| 	output = re.ReplaceAllString(string(outputBytes), "") | ||||
| 	for _, line := range strings.Split(output, "\n") { | ||||
| 		if line == "" { | ||||
| 			continue | ||||
| 	if !conf.Teapot.SkipFailedTable { | ||||
| 		cmd := exec.Command("joint-teapot", "joj3-failed-table", | ||||
| 			envFilePath, conf.Stage.OutputPath, actor, conf.Teapot.GradingRepoName, | ||||
| 			repoName, runNumber, conf.Teapot.FailedTablePath, conf.Name) // #nosec G204
 | ||||
| 		outputBytes, err := cmd.CombinedOutput() | ||||
| 		output := re.ReplaceAllString(string(outputBytes), "") | ||||
| 		for _, line := range strings.Split(output, "\n") { | ||||
| 			if line == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			slog.Info("joint-teapot joj3-failed-table", "output", line) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			slog.Error("joint-teapot joj3-failed-table", "err", err) | ||||
| 			return err | ||||
| 		} | ||||
| 		slog.Info("joint-teapot joj3-create-result-issue", "output", line) | ||||
| 	} | ||||
| 	if err != nil { | ||||
| 		slog.Error("joint-teapot joj3-create-result-issue", "err", err) | ||||
| 		return err | ||||
| 	if !conf.Teapot.SkipIssue { | ||||
| 		cmd := exec.Command("joint-teapot", "joj3-create-result-issue", | ||||
| 			envFilePath, conf.Stage.OutputPath, repoName, runNumber, conf.Name) // #nosec G204
 | ||||
| 		outputBytes, err := cmd.CombinedOutput() | ||||
| 		output := re.ReplaceAllString(string(outputBytes), "") | ||||
| 		for _, line := range strings.Split(output, "\n") { | ||||
| 			if line == "" { | ||||
| 				continue | ||||
| 			} | ||||
| 			slog.Info("joint-teapot joj3-create-result-issue", "output", line) | ||||
| 		} | ||||
| 		if err != nil { | ||||
| 			slog.Error("joint-teapot joj3-create-result-issue", "err", err) | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -150,12 +150,19 @@ func (r ExecutorResult) String() string { | |||
| 	return fmt.Sprintf("%+v", d) | ||||
| } | ||||
| 
 | ||||
| type StageExecutor struct { | ||||
| 	Name string | ||||
| 	Cmds []Cmd | ||||
| } | ||||
| type StageParser struct { | ||||
| 	Name string | ||||
| 	Conf any | ||||
| } | ||||
| 
 | ||||
| type Stage struct { | ||||
| 	Name         string | ||||
| 	ExecutorName string | ||||
| 	ExecutorCmds []Cmd | ||||
| 	ParserName   string | ||||
| 	ParserConf   any | ||||
| 	Name     string | ||||
| 	Executor StageExecutor | ||||
| 	Parsers  []StageParser | ||||
| } | ||||
| 
 | ||||
| type ParserResult struct { | ||||
|  |  | |||
|  | @ -8,51 +8,68 @@ import ( | |||
| func Run(stages []Stage) (stageResults []StageResult, err error) { | ||||
| 	var executorResults []ExecutorResult | ||||
| 	var parserResults []ParserResult | ||||
| 	var tmpParserResults []ParserResult | ||||
| 	var forceQuit bool | ||||
| 	slog.Info("stage run start") | ||||
| 	for _, stage := range stages { | ||||
| 		slog.Info("stage start", "name", stage.Name) | ||||
| 		slog.Info("executor run start", "name", stage.ExecutorName) | ||||
| 		slog.Debug("executor run start", "name", stage.ExecutorName, | ||||
| 			"cmds", stage.ExecutorCmds) | ||||
| 		executor, ok := executorMap[stage.ExecutorName] | ||||
| 		slog.Info("executor run start", "name", stage.Executor.Name) | ||||
| 		slog.Debug("executor run start", "name", stage.Executor.Name, | ||||
| 			"cmds", stage.Executor.Cmds) | ||||
| 		executor, ok := executorMap[stage.Executor.Name] | ||||
| 		if !ok { | ||||
| 			slog.Error("executor not found", "name", stage.ExecutorName) | ||||
| 			err = fmt.Errorf("executor not found: %s", stage.ExecutorName) | ||||
| 			slog.Error("executor not found", "name", stage.Executor.Name) | ||||
| 			err = fmt.Errorf("executor not found: %s", stage.Executor.Name) | ||||
| 			return | ||||
| 		} | ||||
| 		executorResults, err = executor.Run(stage.ExecutorCmds) | ||||
| 		executorResults, err = executor.Run(stage.Executor.Cmds) | ||||
| 		if err != nil { | ||||
| 			slog.Error("executor run error", "name", stage.ExecutorName, "error", err) | ||||
| 			slog.Error("executor run error", "name", stage.Executor.Name, "error", err) | ||||
| 			return | ||||
| 		} | ||||
| 		slog.Debug("executor run done", "results", executorResults) | ||||
| 		for _, executorResult := range executorResults { | ||||
| 			slog.Debug("executor run done", "result.Files", executorResult.Files) | ||||
| 		} | ||||
| 		slog.Info("parser run start", "name", stage.ParserName) | ||||
| 		slog.Debug("parser run start", "name", stage.ParserName, | ||||
| 			"conf", stage.ParserConf) | ||||
| 		parser, ok := parserMap[stage.ParserName] | ||||
| 		if !ok { | ||||
| 			slog.Error("parser not found", "name", stage.ParserName) | ||||
| 			err = fmt.Errorf("parser not found: %s", stage.ParserName) | ||||
| 			return | ||||
| 		parserResults = []ParserResult{} | ||||
| 		stageForceQuit := false | ||||
| 		for _, stageParser := range stage.Parsers { | ||||
| 			slog.Info("parser run start", "name", stageParser.Name) | ||||
| 			slog.Debug("parser run start", "name", stageParser.Name, | ||||
| 				"conf", stageParser.Conf) | ||||
| 			parser, ok := parserMap[stageParser.Name] | ||||
| 			if !ok { | ||||
| 				slog.Error("parser not found", "name", stageParser.Name) | ||||
| 				err = fmt.Errorf("parser not found: %s", stageParser.Name) | ||||
| 				return | ||||
| 			} | ||||
| 			tmpParserResults, forceQuit, err = parser.Run( | ||||
| 				executorResults, stageParser.Conf) | ||||
| 			if err != nil { | ||||
| 				slog.Error("parser run error", "name", stageParser.Name, "error", err) | ||||
| 				return | ||||
| 			} | ||||
| 			stageForceQuit = stageForceQuit || forceQuit | ||||
| 			slog.Debug("parser run done", "results", tmpParserResults) | ||||
| 			if len(parserResults) == 0 { | ||||
| 				parserResults = tmpParserResults | ||||
| 			} else { | ||||
| 				for i := range len(parserResults) { | ||||
| 					parserResults[i].Score += tmpParserResults[i].Score | ||||
| 					parserResults[i].Comment += tmpParserResults[i].Comment | ||||
| 				} | ||||
| 			} | ||||
| 			if forceQuit { | ||||
| 				slog.Error("parser force quit", "name", stageParser.Name) | ||||
| 			} | ||||
| 		} | ||||
| 		parserResults, forceQuit, err = parser.Run(executorResults, stage.ParserConf) | ||||
| 		if err != nil { | ||||
| 			slog.Error("parser run error", "name", stage.ParserName, "error", err) | ||||
| 			return | ||||
| 		} | ||||
| 		slog.Debug("parser run done", "results", parserResults) | ||||
| 		stageResults = append(stageResults, StageResult{ | ||||
| 			Name:      stage.Name, | ||||
| 			Results:   parserResults, | ||||
| 			ForceQuit: forceQuit, | ||||
| 			ForceQuit: stageForceQuit, | ||||
| 		}) | ||||
| 		if forceQuit { | ||||
| 			slog.Error("parser force quit", "name", stage.ParserName) | ||||
| 			return | ||||
| 		if stageForceQuit { | ||||
| 			break | ||||
| 		} | ||||
| 	} | ||||
| 	return | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	Block a user