143 lines
3.3 KiB
Go
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)
|
|
}
|
|
}
|