feat(cmd/joj3): remove hardcoded teapot part (#83) [force build]
All checks were successful
submodules sync / sync (push) Successful in 35s
build / build (push) Successful in 1m11s
build / trigger-build-image (push) Successful in 7s

This commit is contained in:
张泊明518370910136 2025-02-01 08:51:45 -05:00
parent 290b614159
commit 10ff3ebfa1
GPG Key ID: D47306D7062CDA9D
8 changed files with 37 additions and 212 deletions

View File

@ -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.
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.
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

34
cmd/joj3/env/env.go vendored
View File

@ -7,23 +7,27 @@ import (
)
const (
ConfName = "JOJ3_CONF_NAME"
Groups = "JOJ3_GROUPS"
RunID = "JOJ3_RUN_ID"
ConfName = "JOJ3_CONF_NAME"
Groups = "JOJ3_GROUPS"
RunID = "JOJ3_RUN_ID"
CommitMsg = "JOJ3_COMMIT_MSG"
ForceQuitStageName = "JOJ3_FORCE_QUIT_STAGE_NAME"
)
type Attribute struct {
ConfName string
Groups string
RunID string
Actor string
Repository string
Sha string
Ref string
Workflow string
RunNumber string
ActorName string
ActorID string
ConfName string
CommitMsg string
Groups string
RunID string
Actor string
Repository string
Sha string
Ref string
Workflow string
RunNumber string
ActorName string
ActorID string
ForceQuitStageName string
}
var Attr Attribute
@ -51,4 +55,6 @@ func Set() {
os.Setenv(ConfName, Attr.ConfName)
os.Setenv(Groups, Attr.Groups)
os.Setenv(RunID, Attr.RunID)
os.Setenv(CommitMsg, Attr.CommitMsg)
os.Setenv(ForceQuitStageName, Attr.ForceQuitStageName)
}

View File

@ -9,7 +9,6 @@ import (
"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/teapot"
"github.com/joint-online-judge/JOJ3/internal/conf"
internalStage "github.com/joint-online-judge/JOJ3/internal/stage"
)
@ -33,36 +32,7 @@ func init() {
func mainImpl() (err error) {
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
slog.Error("setup slog", "error", err)
return err
@ -78,11 +48,12 @@ func mainImpl() (err error) {
fallbackConfFileName = confFileName
}
slog.Info("start joj3", "version", Version)
commitMsg, err = conf.GetCommitMsg()
commitMsg, err := conf.GetCommitMsg()
if err != nil {
slog.Error("get commit msg", "error", err)
return err
}
env.Attr.CommitMsg = commitMsg
confPath, confStat, conventionalCommit, err := conf.GetConfPath(
confFileRoot, confFileName, fallbackConfFileName, commitMsg, tag)
if err != nil {
@ -118,20 +89,20 @@ func mainImpl() (err error) {
// run stages
groups := conf.MatchGroups(confObj, conventionalCommit)
env.Attr.Groups = strings.Join(groups, ",")
env.Set()
stageResults, forceQuitStageName, err = stage.Run(
confObj, groups,
_, forceQuitStageName, err := stage.Run(
confObj,
groups,
func(
stageResults []internalStage.StageResult,
forceQuitStageName string,
) {
env.Attr.ForceQuitStageName = forceQuitStageName
env.Set()
},
)
if err != nil {
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 != "" {
slog.Info("stage force quit", "name", forceQuitStageName)
return fmt.Errorf("stage force quit with name %s", forceQuitStageName)

View File

@ -132,7 +132,11 @@ func newErrorStageResults(err error) ([]stage.StageResult, string) {
}, "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,
) {
executors.InitWithConf(

View File

@ -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
}

View File

@ -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
}

View File

@ -44,7 +44,6 @@ var (
checkFileNameList string
checkFileSumList string
metaFile []string
confPath string
showVersion *bool
Version string
)
@ -56,7 +55,6 @@ func init() {
flag.StringVar(&checkFileNameList, "checkFileNameList", "", "comma-separated list of files to check")
flag.StringVar(&checkFileSumList, "checkFileSumList", "", "comma-separated list of expected checksums")
parseMultiValueFlag(&metaFile, "meta", "meta files to check")
flag.StringVar(&confPath, "confPath", "", "path to conf file for teapot check") // TODO: remove me
}
func main() {

View File

@ -40,19 +40,6 @@ type Conf struct {
Stages []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 {