feat(cmd/joj3): remove hardcoded teapot part (#83) [force build]
This commit is contained in:
parent
290b614159
commit
10ff3ebfa1
|
@ -95,12 +95,6 @@ These steps are executed in runner-images. We use `sudo -u tt` to elevate the pe
|
||||||
- The parser can return a force quit, which means all the stages after it will be skipped, but the remaining parsers in the current stage will run.
|
- The parser can return a force quit, which means all the stages after it will be skipped, but the remaining parsers in the current stage will run.
|
||||||
5. Generate results.
|
5. Generate results.
|
||||||
- Once the running of stages is done, it will generate a result file where the path is specified in the configuration file.
|
- Once the running of stages is done, it will generate a result file where the path is specified in the configuration file.
|
||||||
2. Run Joint-Teapot.
|
|
||||||
1. Generally speaking, it reads the JOJ3 results file and output results on Gitea.
|
|
||||||
2. With `joint-teapot joj3-all`, it will do the following things:
|
|
||||||
1. Create/Edit an issue in the submitter's repo to show the results.
|
|
||||||
2. Update the scoreboard file in grading repo.
|
|
||||||
3. Update the failed table file in grading repo.
|
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
|
|
34
cmd/joj3/env/env.go
vendored
34
cmd/joj3/env/env.go
vendored
|
@ -7,23 +7,27 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ConfName = "JOJ3_CONF_NAME"
|
ConfName = "JOJ3_CONF_NAME"
|
||||||
Groups = "JOJ3_GROUPS"
|
Groups = "JOJ3_GROUPS"
|
||||||
RunID = "JOJ3_RUN_ID"
|
RunID = "JOJ3_RUN_ID"
|
||||||
|
CommitMsg = "JOJ3_COMMIT_MSG"
|
||||||
|
ForceQuitStageName = "JOJ3_FORCE_QUIT_STAGE_NAME"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Attribute struct {
|
type Attribute struct {
|
||||||
ConfName string
|
ConfName string
|
||||||
Groups string
|
CommitMsg string
|
||||||
RunID string
|
Groups string
|
||||||
Actor string
|
RunID string
|
||||||
Repository string
|
Actor string
|
||||||
Sha string
|
Repository string
|
||||||
Ref string
|
Sha string
|
||||||
Workflow string
|
Ref string
|
||||||
RunNumber string
|
Workflow string
|
||||||
ActorName string
|
RunNumber string
|
||||||
ActorID string
|
ActorName string
|
||||||
|
ActorID string
|
||||||
|
ForceQuitStageName string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Attr Attribute
|
var Attr Attribute
|
||||||
|
@ -51,4 +55,6 @@ func Set() {
|
||||||
os.Setenv(ConfName, Attr.ConfName)
|
os.Setenv(ConfName, Attr.ConfName)
|
||||||
os.Setenv(Groups, Attr.Groups)
|
os.Setenv(Groups, Attr.Groups)
|
||||||
os.Setenv(RunID, Attr.RunID)
|
os.Setenv(RunID, Attr.RunID)
|
||||||
|
os.Setenv(CommitMsg, Attr.CommitMsg)
|
||||||
|
os.Setenv(ForceQuitStageName, Attr.ForceQuitStageName)
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
|
|
||||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/env"
|
"github.com/joint-online-judge/JOJ3/cmd/joj3/env"
|
||||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/stage"
|
"github.com/joint-online-judge/JOJ3/cmd/joj3/stage"
|
||||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/teapot"
|
|
||||||
"github.com/joint-online-judge/JOJ3/internal/conf"
|
"github.com/joint-online-judge/JOJ3/internal/conf"
|
||||||
internalStage "github.com/joint-online-judge/JOJ3/internal/stage"
|
internalStage "github.com/joint-online-judge/JOJ3/internal/stage"
|
||||||
)
|
)
|
||||||
|
@ -33,36 +32,7 @@ func init() {
|
||||||
|
|
||||||
func mainImpl() (err error) {
|
func mainImpl() (err error) {
|
||||||
confObj := new(conf.Conf)
|
confObj := new(conf.Conf)
|
||||||
var stageResults []internalStage.StageResult
|
|
||||||
var forceQuitStageName string
|
|
||||||
var teapotRunResult teapot.RunResult
|
|
||||||
var commitMsg string
|
|
||||||
|
|
||||||
// summarize
|
|
||||||
defer func() {
|
|
||||||
totalScore := 0
|
|
||||||
for _, stageResult := range stageResults {
|
|
||||||
for _, result := range stageResult.Results {
|
|
||||||
totalScore += result.Score
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cappedTotalScore := totalScore
|
|
||||||
if confObj.MaxTotalScore >= 0 {
|
|
||||||
cappedTotalScore = min(totalScore, confObj.MaxTotalScore)
|
|
||||||
}
|
|
||||||
slog.Info(
|
|
||||||
"joj3 summary",
|
|
||||||
"totalScore", totalScore,
|
|
||||||
"cappedTotalScore", cappedTotalScore,
|
|
||||||
"forceQuit", forceQuitStageName != "",
|
|
||||||
"forceQuitStageName", forceQuitStageName,
|
|
||||||
"issue", teapotRunResult.Issue,
|
|
||||||
"action", teapotRunResult.Action,
|
|
||||||
"sha", teapotRunResult.Sha,
|
|
||||||
"commitMsg", commitMsg,
|
|
||||||
"error", err,
|
|
||||||
)
|
|
||||||
}()
|
|
||||||
if err := setupSlog(confObj); err != nil { // before conf is loaded
|
if err := setupSlog(confObj); err != nil { // before conf is loaded
|
||||||
slog.Error("setup slog", "error", err)
|
slog.Error("setup slog", "error", err)
|
||||||
return err
|
return err
|
||||||
|
@ -78,11 +48,12 @@ func mainImpl() (err error) {
|
||||||
fallbackConfFileName = confFileName
|
fallbackConfFileName = confFileName
|
||||||
}
|
}
|
||||||
slog.Info("start joj3", "version", Version)
|
slog.Info("start joj3", "version", Version)
|
||||||
commitMsg, err = conf.GetCommitMsg()
|
commitMsg, err := conf.GetCommitMsg()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("get commit msg", "error", err)
|
slog.Error("get commit msg", "error", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
env.Attr.CommitMsg = commitMsg
|
||||||
confPath, confStat, conventionalCommit, err := conf.GetConfPath(
|
confPath, confStat, conventionalCommit, err := conf.GetConfPath(
|
||||||
confFileRoot, confFileName, fallbackConfFileName, commitMsg, tag)
|
confFileRoot, confFileName, fallbackConfFileName, commitMsg, tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -118,20 +89,20 @@ func mainImpl() (err error) {
|
||||||
// run stages
|
// run stages
|
||||||
groups := conf.MatchGroups(confObj, conventionalCommit)
|
groups := conf.MatchGroups(confObj, conventionalCommit)
|
||||||
env.Attr.Groups = strings.Join(groups, ",")
|
env.Attr.Groups = strings.Join(groups, ",")
|
||||||
env.Set()
|
_, forceQuitStageName, err := stage.Run(
|
||||||
stageResults, forceQuitStageName, err = stage.Run(
|
confObj,
|
||||||
confObj, groups,
|
groups,
|
||||||
|
func(
|
||||||
|
stageResults []internalStage.StageResult,
|
||||||
|
forceQuitStageName string,
|
||||||
|
) {
|
||||||
|
env.Attr.ForceQuitStageName = forceQuitStageName
|
||||||
|
env.Set()
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("stage run", "error", err)
|
slog.Error("stage run", "error", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run teapot
|
|
||||||
teapotRunResult, err = teapot.Run(confObj)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("teapot run", "error", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if forceQuitStageName != "" {
|
if forceQuitStageName != "" {
|
||||||
slog.Info("stage force quit", "name", forceQuitStageName)
|
slog.Info("stage force quit", "name", forceQuitStageName)
|
||||||
return fmt.Errorf("stage force quit with name %s", forceQuitStageName)
|
return fmt.Errorf("stage force quit with name %s", forceQuitStageName)
|
||||||
|
|
|
@ -132,7 +132,11 @@ func newErrorStageResults(err error) ([]stage.StageResult, string) {
|
||||||
}, "Internal Error"
|
}, "Internal Error"
|
||||||
}
|
}
|
||||||
|
|
||||||
func Run(conf *conf.Conf, groups []string) (
|
func Run(
|
||||||
|
conf *conf.Conf,
|
||||||
|
groups []string,
|
||||||
|
onStagesComplete func([]stage.StageResult, string),
|
||||||
|
) (
|
||||||
stageResults []stage.StageResult, forceQuitStageName string, err error,
|
stageResults []stage.StageResult, forceQuitStageName string, err error,
|
||||||
) {
|
) {
|
||||||
executors.InitWithConf(
|
executors.InitWithConf(
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
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
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
package teapot
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log/slog"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/env"
|
|
||||||
"github.com/joint-online-judge/JOJ3/internal/conf"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RunResult struct {
|
|
||||||
Issue int `json:"issue"`
|
|
||||||
Action int `json:"action"`
|
|
||||||
Sha string `json:"sha"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func Run(conf *conf.Conf) (
|
|
||||||
runResult RunResult, err error,
|
|
||||||
) {
|
|
||||||
if conf.Teapot.Skip {
|
|
||||||
slog.Info("teapot skip")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath)
|
|
||||||
if env.Attr.Actor == "" ||
|
|
||||||
env.Attr.Repository == "" ||
|
|
||||||
strings.Count(env.Attr.Repository, "/") != 1 ||
|
|
||||||
env.Attr.RunNumber == "" {
|
|
||||||
slog.Error("teapot env not set")
|
|
||||||
err = fmt.Errorf("teapot env not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
repoParts := strings.Split(env.Attr.Repository, "/")
|
|
||||||
repoName := repoParts[1]
|
|
||||||
skipIssueArg := "--no-skip-result-issue"
|
|
||||||
if conf.Teapot.SkipIssue {
|
|
||||||
skipIssueArg = "--skip-result-issue"
|
|
||||||
}
|
|
||||||
skipScoreboardArg := "--no-skip-scoreboard"
|
|
||||||
if conf.Teapot.SkipScoreboard {
|
|
||||||
skipScoreboardArg = "--skip-scoreboard"
|
|
||||||
}
|
|
||||||
skipFailedTableArg := "--no-skip-failed-table"
|
|
||||||
if conf.Teapot.SkipFailedTable {
|
|
||||||
skipFailedTableArg = "--skip-failed-table"
|
|
||||||
}
|
|
||||||
submitterInIssueTitleArg := "--no-submitter-in-issue-title"
|
|
||||||
if conf.Teapot.SubmitterInIssueTitle {
|
|
||||||
submitterInIssueTitleArg = "--submitter-in-issue-title"
|
|
||||||
}
|
|
||||||
args := []string{
|
|
||||||
"joj3-all", conf.Teapot.EnvFilePath, conf.Stage.OutputPath,
|
|
||||||
env.Attr.Actor, conf.Teapot.GradingRepoName, repoName,
|
|
||||||
env.Attr.RunNumber, conf.Teapot.ScoreboardPath,
|
|
||||||
conf.Teapot.FailedTablePath,
|
|
||||||
conf.Name, env.Attr.Sha, env.Attr.RunID,
|
|
||||||
env.Attr.Groups,
|
|
||||||
"--max-total-score", strconv.Itoa(conf.MaxTotalScore),
|
|
||||||
skipIssueArg, skipScoreboardArg,
|
|
||||||
skipFailedTableArg, submitterInIssueTitleArg,
|
|
||||||
}
|
|
||||||
stdoutBuf, err := runCommand(args)
|
|
||||||
if err != nil {
|
|
||||||
slog.Error("teapot run exec", "error", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if json.Unmarshal(stdoutBuf.Bytes(), &runResult) != nil {
|
|
||||||
slog.Error("unmarshal teapot result", "error", err,
|
|
||||||
"stdout", stdoutBuf.String())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
slog.Info("teapot result", "result", runResult)
|
|
||||||
return
|
|
||||||
}
|
|
|
@ -44,7 +44,6 @@ var (
|
||||||
checkFileNameList string
|
checkFileNameList string
|
||||||
checkFileSumList string
|
checkFileSumList string
|
||||||
metaFile []string
|
metaFile []string
|
||||||
confPath string
|
|
||||||
showVersion *bool
|
showVersion *bool
|
||||||
Version string
|
Version string
|
||||||
)
|
)
|
||||||
|
@ -56,7 +55,6 @@ func init() {
|
||||||
flag.StringVar(&checkFileNameList, "checkFileNameList", "", "comma-separated list of files to check")
|
flag.StringVar(&checkFileNameList, "checkFileNameList", "", "comma-separated list of files to check")
|
||||||
flag.StringVar(&checkFileSumList, "checkFileSumList", "", "comma-separated list of expected checksums")
|
flag.StringVar(&checkFileSumList, "checkFileSumList", "", "comma-separated list of expected checksums")
|
||||||
parseMultiValueFlag(&metaFile, "meta", "meta files to check")
|
parseMultiValueFlag(&metaFile, "meta", "meta files to check")
|
||||||
flag.StringVar(&confPath, "confPath", "", "path to conf file for teapot check") // TODO: remove me
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
@ -40,19 +40,6 @@ type Conf struct {
|
||||||
Stages []ConfStage
|
Stages []ConfStage
|
||||||
PostStages []ConfStage
|
PostStages []ConfStage
|
||||||
}
|
}
|
||||||
Teapot struct {
|
|
||||||
Skip bool `default:"false"`
|
|
||||||
LogPath string `default:"/home/tt/.cache/joint-teapot-debug.log"`
|
|
||||||
EnvFilePath string `default:"/home/tt/.config/teapot/teapot.env"`
|
|
||||||
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"`
|
|
||||||
SubmitterInIssueTitle bool `default:"true"`
|
|
||||||
Groups []ConfGroup
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type OptionalCmd struct {
|
type OptionalCmd struct {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user