Compare commits
No commits in common. "a5767cf040c64c1c1e85f881a3aa9f92f01f94f8" and "762b1aa49df4c4966fa430f5f89877dd0559ba0a" have entirely different histories.
a5767cf040
...
762b1aa49d
6
Makefile
6
Makefile
|
@ -3,9 +3,7 @@
|
||||||
BUILD_DIR = ./build
|
BUILD_DIR = ./build
|
||||||
TMP_DIR = ./tmp
|
TMP_DIR = ./tmp
|
||||||
APPS := $(notdir $(wildcard ./cmd/*))
|
APPS := $(notdir $(wildcard ./cmd/*))
|
||||||
COMMIT_HASH := $(shell git rev-parse --short HEAD)
|
VERSION := $(shell git rev-parse --short HEAD)
|
||||||
DATE := $(shell date +"%Y%m%d-%H%M%S")
|
|
||||||
VERSION := $(COMMIT_HASH)-$(DATE)
|
|
||||||
FLAGS := "-s -w -X main.Version=$(VERSION)"
|
FLAGS := "-s -w -X main.Version=$(VERSION)"
|
||||||
|
|
||||||
all: build
|
all: build
|
||||||
|
@ -32,4 +30,4 @@ test:
|
||||||
ci-test:
|
ci-test:
|
||||||
./scripts/prepare_test_repos.sh $(TMP_DIR)
|
./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"
|
./scripts/run_foreach_test_repos.sh $(TMP_DIR) "sed -i '2i \ \ \"sandboxExecServer\": \"172.17.0.1:5051\",' conf.json"
|
||||||
GITHUB_ACTIONS="test" go test -coverprofile cover.out -v ./...
|
go test -coverprofile cover.out -v ./...
|
||||||
|
|
|
@ -4,16 +4,12 @@
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
1. Clone this repo in a Linux computer. For Windows, use [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install).
|
1. Make sure you are in a Unix-like OS (Linux, MacOS). For Windows, use [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install).
|
||||||
|
|
||||||
```bash
|
|
||||||
$ git clone ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3.git
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Install [Go](https://go.dev/doc/install). Also, make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`.
|
2. Install [Go](https://go.dev/doc/install). Also, make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`.
|
||||||
- If you have problem on connecting to the Go website and Go packages, download Go from [studygolang](https://studygolang.com/dl) and run `go env -w GOPROXY=https://goproxy.io,direct` to set the Go modules mirror proxy after installing Go.
|
- If you have problem on connecting to the Go website and Go packages, download Go from [studygolang](https://studygolang.com/dl) and run `go env -w GOPROXY=https://goproxy.io,direct` to set the Go modules mirror proxy after installing Go.
|
||||||
|
|
||||||
3. Enable cgroup v2 for your OS. For WSL2, check [here](https://stackoverflow.com/a/73376219/13724598). So that you do not need root permission to run `go-judge`.
|
3. Enable cgroup v2 for your OS. Check [here](https://stackoverflow.com/a/73376219/13724598). So that you do not need root permission to run `go-judge`.
|
||||||
|
|
||||||
4. Clone [go-judge](https://github.com/criyle/go-judge).
|
4. Clone [go-judge](https://github.com/criyle/go-judge).
|
||||||
|
|
||||||
|
|
|
@ -13,48 +13,16 @@ import (
|
||||||
"github.com/koding/multiconfig"
|
"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 {
|
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"`
|
SandboxExecServer string `default:"localhost:5051"`
|
||||||
SandboxToken string `default:""`
|
SandboxToken string `default:""`
|
||||||
|
LogPath string `default:""`
|
||||||
OutputPath string `default:"joj3_result.json"`
|
OutputPath string `default:"joj3_result.json"`
|
||||||
GradingRepoName string `default:""`
|
GradingRepoName string `default:""`
|
||||||
SkipTeapot bool `default:"true"`
|
SkipTeapot bool `default:"true"`
|
||||||
ScoreboardPath string `default:"scoreboard.csv"`
|
ScoreboardPath string `default:"scoreboard.csv"`
|
||||||
FailedTablePath string `default:"failed-table.md"`
|
FailedTablePath string `default:"failed-table.md"`
|
||||||
|
Name string `default:"unknown"`
|
||||||
Stages []struct {
|
Stages []struct {
|
||||||
Name string
|
Name string
|
||||||
Group string
|
Group string
|
||||||
|
@ -144,55 +112,25 @@ func parseConventionalCommit(commit string) (*ConventionalCommit, error) {
|
||||||
return cc, nil
|
return cc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseConfFile(path string) (conf *Conf, err error) {
|
func parseConfFile(path string) (conf Conf, err error) {
|
||||||
conf = new(Conf)
|
|
||||||
d := &multiconfig.DefaultLoader{}
|
d := &multiconfig.DefaultLoader{}
|
||||||
d.Loader = multiconfig.MultiLoader(
|
d.Loader = multiconfig.MultiLoader(
|
||||||
&multiconfig.TagLoader{},
|
&multiconfig.TagLoader{},
|
||||||
&multiconfig.JSONLoader{Path: path},
|
&multiconfig.JSONLoader{Path: path},
|
||||||
)
|
)
|
||||||
d.Validator = multiconfig.MultiValidator(&multiconfig.RequiredValidator{})
|
d.Validator = multiconfig.MultiValidator(&multiconfig.RequiredValidator{})
|
||||||
if err = d.Load(conf); err != nil {
|
if err = d.Load(&conf); err != nil {
|
||||||
slog.Error("parse stages conf", "error", err)
|
slog.Error("parse stages conf", "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err = d.Validate(conf); err != nil {
|
if err = d.Validate(&conf); err != nil {
|
||||||
slog.Error("validate stages conf", "error", err)
|
slog.Error("validate stages conf", "error", err)
|
||||||
return
|
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,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
conf.Teapot.GradingRepoName = conf.GradingRepoName
|
|
||||||
conf.Teapot.ScoreboardPath = conf.ScoreboardPath
|
|
||||||
conf.Teapot.FailedTablePath = conf.FailedTablePath
|
|
||||||
if conf.SkipTeapot {
|
|
||||||
conf.Teapot.SkipScoreboard = true
|
|
||||||
conf.Teapot.SkipFailedTable = true
|
|
||||||
conf.Teapot.SkipIssue = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseMsg(confRoot, confName, msg string) (confPath, group string, err error) {
|
func ParseMsg(confRoot, confName, msg string) (conf Conf, group string, err error) {
|
||||||
slog.Info("parse msg", "msg", msg)
|
slog.Info("parse msg", "msg", msg)
|
||||||
conventionalCommit, err := parseConventionalCommit(msg)
|
conventionalCommit, err := parseConventionalCommit(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -200,7 +138,7 @@ func ParseMsg(confRoot, confName, msg string) (confPath, group string, err error
|
||||||
}
|
}
|
||||||
slog.Info("conventional commit", "commit", conventionalCommit)
|
slog.Info("conventional commit", "commit", conventionalCommit)
|
||||||
confRoot = filepath.Clean(confRoot)
|
confRoot = filepath.Clean(confRoot)
|
||||||
confPath = filepath.Clean(fmt.Sprintf("%s/%s/%s",
|
confPath := filepath.Clean(fmt.Sprintf("%s/%s/%s",
|
||||||
confRoot, conventionalCommit.Scope, confName))
|
confRoot, conventionalCommit.Scope, confName))
|
||||||
relPath, err := filepath.Rel(confRoot, confPath)
|
relPath, err := filepath.Rel(confRoot, confPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -210,6 +148,11 @@ func ParseMsg(confRoot, confName, msg string) (confPath, group string, err error
|
||||||
err = fmt.Errorf("invalid scope as path: %s", conventionalCommit.Scope)
|
err = fmt.Errorf("invalid scope as path: %s", conventionalCommit.Scope)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
slog.Info("try to load conf", "path", confPath)
|
||||||
|
conf, err = parseConfFile(confPath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
groupKeywords := []string{"joj"}
|
groupKeywords := []string{"joj"}
|
||||||
for _, groupKeyword := range groupKeywords {
|
for _, groupKeyword := range groupKeywords {
|
||||||
if strings.Contains(
|
if strings.Contains(
|
||||||
|
@ -218,6 +161,7 @@ func ParseMsg(confRoot, confName, msg string) (confPath, group string, err error
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
slog.Debug("conf loaded", "conf", conf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ func mainImpl() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
confPath, group, err := conf.ParseMsg(confRoot, confName, msg)
|
confObj, group, err := conf.ParseMsg(confRoot, confName, msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("parse msg", "error", err)
|
slog.Error("parse msg", "error", err)
|
||||||
validScopes, scopeErr := conf.ListValidScopes(
|
validScopes, scopeErr := conf.ListValidScopes(
|
||||||
|
@ -58,13 +58,6 @@ func mainImpl() error {
|
||||||
"valid scopes", validScopes)
|
"valid scopes", validScopes)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
slog.Info("try to load conf", "path", confPath)
|
|
||||||
confObj, err := conf.ParseConfFile(confPath)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("parse conf", "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
slog.Debug("conf loaded", "conf", confObj)
|
|
||||||
if err := setupSlog(confObj.LogPath); err != nil { // after conf is loaded
|
if err := setupSlog(confObj.LogPath); err != nil { // after conf is loaded
|
||||||
slog.Error("setup slog", "error", err)
|
slog.Error("setup slog", "error", err)
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -13,10 +13,10 @@ import (
|
||||||
"github.com/jinzhu/copier"
|
"github.com/jinzhu/copier"
|
||||||
)
|
)
|
||||||
|
|
||||||
func generateStages(conf *conf.Conf, group string) ([]stage.Stage, error) {
|
func generateStages(conf conf.Conf, group string) ([]stage.Stage, error) {
|
||||||
stages := []stage.Stage{}
|
stages := []stage.Stage{}
|
||||||
existNames := map[string]bool{}
|
existNames := map[string]bool{}
|
||||||
for _, s := range conf.Stage.Stages {
|
for _, s := range conf.Stages {
|
||||||
if s.Group != "" && group != s.Group {
|
if s.Group != "" && group != s.Group {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -50,20 +50,12 @@ func generateStages(conf *conf.Conf, group string) ([]stage.Stage, error) {
|
||||||
if len(s.Executor.With.Cases) == 0 {
|
if len(s.Executor.With.Cases) == 0 {
|
||||||
cmds = []stage.Cmd{defaultCmd}
|
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{
|
stages = append(stages, stage.Stage{
|
||||||
Name: s.Name,
|
Name: s.Name,
|
||||||
Executor: stage.StageExecutor{
|
ExecutorName: s.Executor.Name,
|
||||||
Name: s.Executor.Name,
|
ExecutorCmds: cmds,
|
||||||
Cmds: cmds,
|
ParserName: s.Parser.Name,
|
||||||
},
|
ParserConf: s.Parser.With,
|
||||||
Parsers: parsers,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
slog.Debug("stages generated", "stages", stages)
|
slog.Debug("stages generated", "stages", stages)
|
||||||
|
@ -81,11 +73,8 @@ func outputResult(outputPath string, results []stage.StageResult) error {
|
||||||
append(content, []byte("\n")...), 0o600)
|
append(content, []byte("\n")...), 0o600)
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(conf *conf.Conf, group string) error {
|
func Run(conf conf.Conf, group string) error {
|
||||||
executors.InitWithConf(
|
executors.InitWithConf(conf.SandboxExecServer, conf.SandboxToken)
|
||||||
conf.Stage.SandboxExecServer,
|
|
||||||
conf.Stage.SandboxToken,
|
|
||||||
)
|
|
||||||
stages, err := generateStages(conf, group)
|
stages, err := generateStages(conf, group)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("generate stages", "error", err)
|
slog.Error("generate stages", "error", err)
|
||||||
|
@ -97,7 +86,7 @@ func Run(conf *conf.Conf, group string) error {
|
||||||
slog.Error("run stages", "error", err)
|
slog.Error("run stages", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := outputResult(conf.Stage.OutputPath, results); err != nil {
|
if err := outputResult(conf.OutputPath, results); err != nil {
|
||||||
slog.Error("output result", "error", err)
|
slog.Error("output result", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,22 +7,17 @@ import (
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/conf"
|
"github.com/joint-online-judge/JOJ3/cmd/joj3/conf"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Run(conf *conf.Conf) error {
|
func Run(conf conf.Conf) error {
|
||||||
actions := os.Getenv("GITHUB_ACTIONS")
|
if conf.SkipTeapot {
|
||||||
if actions != "true" {
|
|
||||||
slog.Info("teapot exit", "GITHUB_ACTIONS", actions)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath)
|
os.Setenv("LOG_FILE_PATH", "/home/tt/.cache/joint-teapot-debug.log")
|
||||||
os.Setenv("_TYPER_STANDARD_TRACEBACK", "1")
|
os.Setenv("_TYPER_STANDARD_TRACEBACK", "1")
|
||||||
envFilePath := "/home/tt/.config/teapot/teapot.env"
|
envFilePath := "/home/tt/.config/teapot/teapot.env"
|
||||||
// TODO: pass sha to joint-teapot
|
|
||||||
// sha := os.Getenv("GITHUB_SHA")
|
|
||||||
actor := os.Getenv("GITHUB_ACTOR")
|
actor := os.Getenv("GITHUB_ACTOR")
|
||||||
repository := os.Getenv("GITHUB_REPOSITORY")
|
repository := os.Getenv("GITHUB_REPOSITORY")
|
||||||
runNumber := os.Getenv("GITHUB_RUN_NUMBER")
|
runNumber := os.Getenv("GITHUB_RUN_NUMBER")
|
||||||
|
@ -34,61 +29,49 @@ func Run(conf *conf.Conf) error {
|
||||||
repoParts := strings.Split(repository, "/")
|
repoParts := strings.Split(repository, "/")
|
||||||
repoName := repoParts[1]
|
repoName := repoParts[1]
|
||||||
re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
|
re := regexp.MustCompile(`\x1b\[[0-9;]*[a-zA-Z]`)
|
||||||
execCommand := func(name string, cmdArgs []string) error {
|
cmd := exec.Command("joint-teapot", "joj3-scoreboard",
|
||||||
cmd := exec.Command(name, cmdArgs...) // #nosec G204
|
envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName,
|
||||||
outputBytes, err := cmd.CombinedOutput()
|
runNumber, conf.ScoreboardPath, conf.Name) // #nosec G204
|
||||||
output := re.ReplaceAllString(string(outputBytes), "")
|
outputBytes, err := cmd.CombinedOutput()
|
||||||
for _, line := range strings.Split(output, "\n") {
|
output := re.ReplaceAllString(string(outputBytes), "")
|
||||||
if line == "" {
|
for _, line := range strings.Split(output, "\n") {
|
||||||
continue
|
if line == "" {
|
||||||
}
|
continue
|
||||||
slog.Info(fmt.Sprintf("%s %s", name, cmdArgs[0]), "output", line)
|
|
||||||
}
|
}
|
||||||
|
slog.Info("joint-teapot joj3-scoreboard", "output", line)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("joint-teapot joj3-scoreboard", "err", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
var wg sync.WaitGroup
|
cmd = exec.Command("joint-teapot", "joj3-failed-table",
|
||||||
var scoreboardErr, failedTableErr, issueErr error
|
envFilePath, conf.OutputPath, actor, conf.GradingRepoName, repoName,
|
||||||
wg.Add(2)
|
runNumber, conf.FailedTablePath, conf.Name) // #nosec G204
|
||||||
go func() {
|
outputBytes, err = cmd.CombinedOutput()
|
||||||
defer wg.Done()
|
output = re.ReplaceAllString(string(outputBytes), "")
|
||||||
if !conf.Teapot.SkipScoreboard {
|
for _, line := range strings.Split(output, "\n") {
|
||||||
err := execCommand("joint-teapot", []string{
|
if line == "" {
|
||||||
"joj3-scoreboard", envFilePath, conf.Stage.OutputPath, actor,
|
continue
|
||||||
conf.Teapot.GradingRepoName, repoName, runNumber,
|
|
||||||
conf.Teapot.ScoreboardPath, conf.Name,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
scoreboardErr = err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if !conf.Teapot.SkipFailedTable {
|
slog.Info("joint-teapot joj3-failed-table", "output", line)
|
||||||
err := execCommand("joint-teapot", []string{
|
}
|
||||||
"joj3-failed-table", envFilePath, conf.Stage.OutputPath, actor,
|
if err != nil {
|
||||||
conf.Teapot.GradingRepoName, repoName, runNumber,
|
slog.Error("joint-teapot joj3-failed-table", "err", err)
|
||||||
conf.Teapot.FailedTablePath, conf.Name,
|
return err
|
||||||
})
|
}
|
||||||
if err != nil {
|
cmd = exec.Command("joint-teapot", "joj3-create-result-issue",
|
||||||
failedTableErr = err
|
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
|
||||||
}
|
}
|
||||||
}()
|
slog.Info("joint-teapot joj3-create-result-issue", "output", line)
|
||||||
go func() {
|
}
|
||||||
defer wg.Done()
|
if err != nil {
|
||||||
if !conf.Teapot.SkipIssue {
|
slog.Error("joint-teapot joj3-create-result-issue", "err", err)
|
||||||
err := execCommand("joint-teapot", []string{
|
return err
|
||||||
"joj3-create-result-issue", envFilePath, conf.Stage.OutputPath,
|
|
||||||
repoName, runNumber, conf.Name,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
issueErr = err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
if scoreboardErr != nil || failedTableErr != nil || issueErr != nil {
|
|
||||||
slog.Error("teapot exit", "scoreboardErr", scoreboardErr,
|
|
||||||
"failedTableErr", failedTableErr, "issueErr", issueErr)
|
|
||||||
return fmt.Errorf("teapot exit")
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,7 +44,6 @@ func main() {
|
||||||
checkRelease := flag.Bool("checkRelease", true, "trigger release check")
|
checkRelease := flag.Bool("checkRelease", true, "trigger release check")
|
||||||
rootDir := flag.String("root", "", "")
|
rootDir := flag.String("root", "", "")
|
||||||
repo := flag.String("repo", "", "")
|
repo := flag.String("repo", "", "")
|
||||||
size := flag.Float64("reposize", 2, "size of the repo")
|
|
||||||
localList := flag.String("localList", "", "")
|
localList := flag.String("localList", "", "")
|
||||||
droneBranch := flag.String("droneBranch", "", "")
|
droneBranch := flag.String("droneBranch", "", "")
|
||||||
releaseCategories := flag.String("releaseCategories", "", "")
|
releaseCategories := flag.String("releaseCategories", "", "")
|
||||||
|
@ -60,7 +59,7 @@ func main() {
|
||||||
}
|
}
|
||||||
setupSlog()
|
setupSlog()
|
||||||
var err error
|
var err error
|
||||||
err = healthcheck.RepoSize(*size)
|
err = healthcheck.RepoSize()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("### Repo Size Check Failed:\n%s\n", err.Error())
|
fmt.Printf("### Repo Size Check Failed:\n%s\n", err.Error())
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,9 +21,7 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
PassComment string `default:"🥳Passed!\n"`
|
Cases []struct {
|
||||||
FailComment string `default:"🧐Failed...\n"`
|
|
||||||
Cases []struct {
|
|
||||||
IgnoreResultStatus bool
|
IgnoreResultStatus bool
|
||||||
Outputs []struct {
|
Outputs []struct {
|
||||||
Score int
|
Score int
|
||||||
|
@ -74,9 +72,9 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
if compareChars(string(answer), result.Files[output.FileName],
|
if compareChars(string(answer), result.Files[output.FileName],
|
||||||
output.CompareSpace) {
|
output.CompareSpace) {
|
||||||
score += output.Score
|
score += output.Score
|
||||||
comment += conf.PassComment
|
comment += "Pass!\n"
|
||||||
} else {
|
} else {
|
||||||
comment += conf.FailComment
|
comment += "Fail!\n"
|
||||||
comment += fmt.Sprintf("Difference found in `%s`.\n",
|
comment += fmt.Sprintf("Difference found in `%s`.\n",
|
||||||
output.FileName)
|
output.FileName)
|
||||||
if !output.AlwaysHide {
|
if !output.AlwaysHide {
|
||||||
|
|
|
@ -29,27 +29,27 @@ func (*ResultDetail) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
var res []stage.ParserResult
|
var res []stage.ParserResult
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
comment := ""
|
comment := ""
|
||||||
|
// TODO: more human readable units
|
||||||
if conf.ShowExitStatus {
|
if conf.ShowExitStatus {
|
||||||
comment += fmt.Sprintf("Exit Status: `%d`\n", result.ExitStatus)
|
comment += fmt.Sprintf("Exit Status: %d\n", result.ExitStatus)
|
||||||
}
|
}
|
||||||
if conf.ShowError {
|
if conf.ShowError {
|
||||||
comment += fmt.Sprintf("Error: `%s`\n", result.Error)
|
comment += fmt.Sprintf("Error: %s\n", result.Error)
|
||||||
}
|
}
|
||||||
if conf.ShowTime {
|
if conf.ShowTime {
|
||||||
comment += fmt.Sprintf("Time: `%d ms`\n", result.Time/1e9)
|
comment += fmt.Sprintf("Time: %d\n", result.Time)
|
||||||
}
|
}
|
||||||
if conf.ShowMemory {
|
if conf.ShowMemory {
|
||||||
comment += fmt.Sprintf("Memory: `%.2f MiB`\n",
|
comment += fmt.Sprintf("Memory: %d\n", result.Memory)
|
||||||
float64(result.Memory)/(1024*1024))
|
|
||||||
}
|
}
|
||||||
if conf.ShowRunTime {
|
if conf.ShowRunTime {
|
||||||
comment += fmt.Sprintf("RunTime: `%d ms`\n", result.RunTime/1e9)
|
comment += fmt.Sprintf("RunTime: %d\n", result.RunTime)
|
||||||
}
|
}
|
||||||
for _, file := range conf.ShowFiles {
|
for _, file := range conf.ShowFiles {
|
||||||
content, ok := result.Files[file]
|
content, ok := result.Files[file]
|
||||||
comment += fmt.Sprintf("File `%s`:\n", file)
|
comment += fmt.Sprintf("File: `%s`.\n", file)
|
||||||
if ok {
|
if ok {
|
||||||
comment += fmt.Sprintf("```\n%s\n```\n", content)
|
comment += fmt.Sprintf("```%s```\n", content)
|
||||||
} else {
|
} else {
|
||||||
comment += "Not found.\n"
|
comment += "Not found.\n"
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,19 +150,12 @@ func (r ExecutorResult) String() string {
|
||||||
return fmt.Sprintf("%+v", d)
|
return fmt.Sprintf("%+v", d)
|
||||||
}
|
}
|
||||||
|
|
||||||
type StageExecutor struct {
|
|
||||||
Name string
|
|
||||||
Cmds []Cmd
|
|
||||||
}
|
|
||||||
type StageParser struct {
|
|
||||||
Name string
|
|
||||||
Conf any
|
|
||||||
}
|
|
||||||
|
|
||||||
type Stage struct {
|
type Stage struct {
|
||||||
Name string
|
Name string
|
||||||
Executor StageExecutor
|
ExecutorName string
|
||||||
Parsers []StageParser
|
ExecutorCmds []Cmd
|
||||||
|
ParserName string
|
||||||
|
ParserConf any
|
||||||
}
|
}
|
||||||
|
|
||||||
type ParserResult struct {
|
type ParserResult struct {
|
||||||
|
|
|
@ -8,68 +8,51 @@ import (
|
||||||
func Run(stages []Stage) (stageResults []StageResult, err error) {
|
func Run(stages []Stage) (stageResults []StageResult, err error) {
|
||||||
var executorResults []ExecutorResult
|
var executorResults []ExecutorResult
|
||||||
var parserResults []ParserResult
|
var parserResults []ParserResult
|
||||||
var tmpParserResults []ParserResult
|
|
||||||
var forceQuit bool
|
var forceQuit bool
|
||||||
slog.Info("stage run start")
|
slog.Info("stage run start")
|
||||||
for _, stage := range stages {
|
for _, stage := range stages {
|
||||||
slog.Info("stage start", "name", stage.Name)
|
slog.Info("stage start", "name", stage.Name)
|
||||||
slog.Info("executor run start", "name", stage.Executor.Name)
|
slog.Info("executor run start", "name", stage.ExecutorName)
|
||||||
slog.Debug("executor run start", "name", stage.Executor.Name,
|
slog.Debug("executor run start", "name", stage.ExecutorName,
|
||||||
"cmds", stage.Executor.Cmds)
|
"cmds", stage.ExecutorCmds)
|
||||||
executor, ok := executorMap[stage.Executor.Name]
|
executor, ok := executorMap[stage.ExecutorName]
|
||||||
if !ok {
|
if !ok {
|
||||||
slog.Error("executor not found", "name", stage.Executor.Name)
|
slog.Error("executor not found", "name", stage.ExecutorName)
|
||||||
err = fmt.Errorf("executor not found: %s", stage.Executor.Name)
|
err = fmt.Errorf("executor not found: %s", stage.ExecutorName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
executorResults, err = executor.Run(stage.Executor.Cmds)
|
executorResults, err = executor.Run(stage.ExecutorCmds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("executor run error", "name", stage.Executor.Name, "error", err)
|
slog.Error("executor run error", "name", stage.ExecutorName, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
slog.Debug("executor run done", "results", executorResults)
|
slog.Debug("executor run done", "results", executorResults)
|
||||||
for _, executorResult := range executorResults {
|
for _, executorResult := range executorResults {
|
||||||
slog.Debug("executor run done", "result.Files", executorResult.Files)
|
slog.Debug("executor run done", "result.Files", executorResult.Files)
|
||||||
}
|
}
|
||||||
parserResults = []ParserResult{}
|
slog.Info("parser run start", "name", stage.ParserName)
|
||||||
stageForceQuit := false
|
slog.Debug("parser run start", "name", stage.ParserName,
|
||||||
for _, stageParser := range stage.Parsers {
|
"conf", stage.ParserConf)
|
||||||
slog.Info("parser run start", "name", stageParser.Name)
|
parser, ok := parserMap[stage.ParserName]
|
||||||
slog.Debug("parser run start", "name", stageParser.Name,
|
if !ok {
|
||||||
"conf", stageParser.Conf)
|
slog.Error("parser not found", "name", stage.ParserName)
|
||||||
parser, ok := parserMap[stageParser.Name]
|
err = fmt.Errorf("parser not found: %s", stage.ParserName)
|
||||||
if !ok {
|
return
|
||||||
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{
|
stageResults = append(stageResults, StageResult{
|
||||||
Name: stage.Name,
|
Name: stage.Name,
|
||||||
Results: parserResults,
|
Results: parserResults,
|
||||||
ForceQuit: stageForceQuit,
|
ForceQuit: forceQuit,
|
||||||
})
|
})
|
||||||
if stageForceQuit {
|
if forceQuit {
|
||||||
break
|
slog.Error("parser force quit", "name", stage.ParserName)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
|
|
||||||
// RepoSize checks the size of the repository to determine if it is oversized.
|
// RepoSize checks the size of the repository to determine if it is oversized.
|
||||||
// It executes the 'git count-objects -v' command to obtain the size information,
|
// It executes the 'git count-objects -v' command to obtain the size information,
|
||||||
func RepoSize(confSize float64) error {
|
func RepoSize() error {
|
||||||
// TODO: reimplement here when go-git is available
|
// TODO: reimplement here when go-git is available
|
||||||
// https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md
|
// https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md
|
||||||
cmd := exec.Command("git", "count-objects", "-v")
|
cmd := exec.Command("git", "count-objects", "-v")
|
||||||
|
@ -33,8 +33,8 @@ func RepoSize(confSize float64) error {
|
||||||
sum += size
|
sum += size
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sum > int(confSize*1024) {
|
if sum > 2048 {
|
||||||
return fmt.Errorf("Repository larger than %.1f MiB. Please clean up or contact the teaching team.", confSize)
|
return fmt.Errorf("Repository larger than 2MB. Please clean up or contact the teaching team.")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user