JOJ3/cmd/joj3/main.go
张泊明518370910136 b40072a485
All checks were successful
build-image / create-empty-commit (push) Successful in 5s
checks / build (push) Successful in 1m19s
feat: rename module
2024-09-30 22:04:18 -04:00

143 lines
3.3 KiB
Go

package main
import (
"encoding/json"
"flag"
"log/slog"
"os"
"github.com/joint-online-judge/JOJ3/internal/executors"
_ "github.com/joint-online-judge/JOJ3/internal/parsers"
"github.com/joint-online-judge/JOJ3/internal/stage"
"github.com/go-git/go-git/v5"
"github.com/jinzhu/copier"
)
func setupSlog(logLevel int) {
lvl := new(slog.LevelVar)
lvl.Set(slog.Level(logLevel))
opts := &slog.HandlerOptions{Level: lvl}
handler := slog.NewTextHandler(os.Stderr, opts)
logger := slog.New(handler)
slog.SetDefault(logger)
}
func getCommitMsg() (msg string, 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
}
msg = commit.Message
return
}
func generateStages(conf Conf) ([]stage.Stage, error) {
stages := []stage.Stage{}
for _, s := range conf.Stages {
var cmds []stage.Cmd
defaultCmd := s.Executor.With.Default
for _, optionalCmd := range s.Executor.With.Cases {
cmd := s.Executor.With.Default
err := copier.Copy(&cmd, &optionalCmd)
if err != nil {
slog.Error("generate stages", "error", err)
return stages, err
}
// since these 3 values are pointers, copier will always copy
// them, so we need to check them manually
if defaultCmd.Stdin != nil && optionalCmd.Stdin == nil {
cmd.Stdin = defaultCmd.Stdin
}
if defaultCmd.Stdout != nil && optionalCmd.Stdout == nil {
cmd.Stdout = defaultCmd.Stdout
}
if defaultCmd.Stderr != nil && optionalCmd.Stderr == nil {
cmd.Stderr = defaultCmd.Stderr
}
cmds = append(cmds, cmd)
}
if len(s.Executor.With.Cases) == 0 {
cmds = []stage.Cmd{defaultCmd}
}
slog.Debug("parse stages conf", "cmds", cmds)
stages = append(stages, stage.Stage{
Name: s.Name,
ExecutorName: s.Executor.Name,
ExecutorCmds: cmds,
ParserName: s.Parser.Name,
ParserConf: s.Parser.With,
})
}
return stages, nil
}
func outputResult(outputPath string, results []stage.StageResult) error {
content, err := json.Marshal(results)
if err != nil {
return err
}
return os.WriteFile(outputPath,
append(content, []byte("\n")...), 0o600)
}
var (
metaConfPath string
msg string
)
func init() {
flag.StringVar(&metaConfPath, "meta-conf", "meta-conf.toml", "meta config file path")
flag.StringVar(&msg, "msg", "", "message to trigger the running, leave empty to use git commit message on HEAD")
}
func mainImpl() error {
setupSlog(int(slog.LevelInfo)) // before conf is loaded
flag.Parse()
if msg == "" {
var err error
msg, err = getCommitMsg()
if err != nil {
slog.Error("get commit msg", "error", err)
return err
}
}
conf, err := msgToConf(metaConfPath, msg)
if err != nil {
slog.Error("no conf found", "error", err)
return err
}
setupSlog(conf.LogLevel) // after conf is loaded
executors.InitWithConf(conf.SandboxExecServer, conf.SandboxToken)
stages, err := generateStages(conf)
if err != nil {
slog.Error("generate stages", "error", err)
return err
}
defer stage.Cleanup()
results, err := stage.Run(stages)
if err != nil {
slog.Error("run stages", "error", err)
return err
}
if err := outputResult(conf.OutputPath, results); err != nil {
slog.Error("output result", "error", err)
return err
}
return nil
}
func main() {
if err := mainImpl(); err != nil {
os.Exit(1)
}
}