feat(healthcheck): add teapot-checker to repo-health-checker
This commit is contained in:
parent
648fe7d0a4
commit
0f5ccf8af2
|
@ -1,11 +1,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/joint-online-judge/JOJ3/internal/conf"
|
||||
"github.com/joint-online-judge/JOJ3/pkg/healthcheck"
|
||||
)
|
||||
|
||||
|
@ -43,6 +46,7 @@ var (
|
|||
checkFileSumList string
|
||||
metaFile []string
|
||||
gitWhitelist []string
|
||||
confPath string
|
||||
showVersion *bool
|
||||
Version string
|
||||
)
|
||||
|
@ -58,9 +62,42 @@ func init() {
|
|||
parseMultiValueFlag(&metaFile, "meta", "meta files to check")
|
||||
// TODO: remove gitWhitelist, it is only for backward compatibility now
|
||||
parseMultiValueFlag(&gitWhitelist, "whitelist", "[DEPRECATED] will be ignored")
|
||||
flag.StringVar(&confPath, "confPath", "", "path to conf file for teapot check")
|
||||
}
|
||||
|
||||
func prepareTeapotCheck() (
|
||||
confObj *conf.Conf, groups []string, actor, repoName string, err error,
|
||||
) {
|
||||
actor = os.Getenv("GITHUB_ACTOR")
|
||||
repository := os.Getenv("GITHUB_REPOSITORY")
|
||||
if actor == "" ||
|
||||
repository != "" ||
|
||||
strings.Count(repository, "/") != 1 ||
|
||||
confPath != "" {
|
||||
err = fmt.Errorf("teapot env not set")
|
||||
return
|
||||
}
|
||||
repoParts := strings.Split(repository, "/")
|
||||
repoName = repoParts[1]
|
||||
commitMsg, err := conf.GetCommitMsg()
|
||||
if err != nil {
|
||||
slog.Error("get commit msg", "error", err)
|
||||
return
|
||||
}
|
||||
conventionalCommit, err := conf.ParseConventionalCommit(commitMsg)
|
||||
if err != nil {
|
||||
slog.Error("parse commit msg", "error", err)
|
||||
return
|
||||
}
|
||||
confObj, _, err = conf.ParseConfFile(confPath)
|
||||
if err != nil {
|
||||
slog.Error("parse conf", "error", err)
|
||||
return
|
||||
}
|
||||
groups = conf.MatchGroups(confObj, conventionalCommit)
|
||||
return
|
||||
}
|
||||
|
||||
// Generally, err is used for runtime errors, and checkRes is used for the result of the checks.
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if *showVersion {
|
||||
|
@ -77,28 +114,19 @@ func main() {
|
|||
"meta", metaFile,
|
||||
)
|
||||
var err error
|
||||
err = healthcheck.RepoSize(repoSize)
|
||||
confObj, groups, actor, repoName, err := prepareTeapotCheck()
|
||||
if err != nil {
|
||||
fmt.Printf("### Repo Size Check Failed:\n%s\n", err.Error())
|
||||
slog.Error("prepare teapot check", "error", err)
|
||||
confObj = nil
|
||||
}
|
||||
err = healthcheck.ForbiddenCheck(rootDir)
|
||||
res := healthcheck.All(
|
||||
confObj, actor, repoName, rootDir, checkFileNameList, checkFileSumList,
|
||||
groups, metaFile, repoSize,
|
||||
)
|
||||
jsonRes, err := json.Marshal(res)
|
||||
if err != nil {
|
||||
fmt.Printf("### Forbidden File Check Failed:\n%s\n", err.Error())
|
||||
}
|
||||
err = healthcheck.MetaCheck(rootDir, metaFile)
|
||||
if err != nil {
|
||||
fmt.Printf("### Meta File Check Failed:\n%s\n", err.Error())
|
||||
}
|
||||
err = healthcheck.NonAsciiFiles(rootDir)
|
||||
if err != nil {
|
||||
fmt.Printf("### Non-ASCII Characters File Check Failed:\n%s\n", err.Error())
|
||||
}
|
||||
err = healthcheck.NonAsciiMsg(rootDir)
|
||||
if err != nil {
|
||||
fmt.Printf("### Non-ASCII Characters Commit Message Check Failed:\n%s\n", err.Error())
|
||||
}
|
||||
err = healthcheck.VerifyFiles(rootDir, checkFileNameList, checkFileSumList)
|
||||
if err != nil {
|
||||
fmt.Printf("### Repo File Check Failed:\n%s\n", err.Error())
|
||||
slog.Error("marshal result", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(string(jsonRes))
|
||||
}
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package healthcheck
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/criyle/go-judge/envexec"
|
||||
"github.com/joint-online-judge/JOJ3/internal/stage"
|
||||
"github.com/joint-online-judge/JOJ3/pkg/healthcheck"
|
||||
)
|
||||
|
||||
type Healthcheck struct{}
|
||||
|
@ -21,15 +23,28 @@ func Parse(executorResult stage.ExecutorResult, conf Conf) (stage.ParserResult,
|
|||
return stage.ParserResult{
|
||||
Score: 0,
|
||||
Comment: fmt.Sprintf(
|
||||
"Unexpected executor status: `%s`\n`stdout`:\n```%s\n```\n`stderr`:\n```%s\n```",
|
||||
executorResult.Status, stdout, stderr,
|
||||
"Unexpected executor status: `%s`\n`stderr`:\n```%s\n```\n",
|
||||
executorResult.Status, stderr,
|
||||
),
|
||||
}, true
|
||||
}
|
||||
var res healthcheck.Result
|
||||
err := json.Unmarshal([]byte(stdout), &res)
|
||||
if err != nil {
|
||||
return stage.ParserResult{
|
||||
Score: 0,
|
||||
Comment: fmt.Sprintf(
|
||||
"Failed to parse result: `%s`\n`stderr`:\n```%s\n```\n",
|
||||
err, stderr,
|
||||
),
|
||||
}, true
|
||||
}
|
||||
comment := res.Msg
|
||||
forceQuit := res.Failed
|
||||
return stage.ParserResult{
|
||||
Score: 0,
|
||||
Comment: stdout,
|
||||
}, stdout != ""
|
||||
Comment: comment,
|
||||
}, forceQuit
|
||||
}
|
||||
|
||||
func (*Healthcheck) Run(results []stage.ExecutorResult, confAny any) (
|
||||
|
|
61
pkg/healthcheck/all.go
Normal file
61
pkg/healthcheck/all.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package healthcheck
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/joint-online-judge/JOJ3/internal/conf"
|
||||
)
|
||||
|
||||
type Result struct {
|
||||
Msg string
|
||||
Failed bool
|
||||
}
|
||||
|
||||
func All(
|
||||
confObj *conf.Conf,
|
||||
actor, repoName, rootDir, checkFileNameList, checkFileSumList string,
|
||||
groups, metaFile []string,
|
||||
repoSize float64,
|
||||
) (res Result) {
|
||||
var err error
|
||||
if confObj != nil {
|
||||
output, err := TeapotCheck(confObj, actor, repoName, groups)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Teapot Check Failed:\n%s\n", output)
|
||||
res.Failed = true
|
||||
} else {
|
||||
res.Msg += fmt.Sprintf("### Teapot Check Result:\n%s\n", output)
|
||||
}
|
||||
}
|
||||
err = RepoSize(repoSize)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Repo Size Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
err = ForbiddenCheck(rootDir)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Forbidden File Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
err = MetaCheck(rootDir, metaFile)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Meta File Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
err = NonAsciiFiles(rootDir)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Non-ASCII Characters File Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
err = NonAsciiMsg(rootDir)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Non-ASCII Characters Commit Message Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
err = VerifyFiles(rootDir, checkFileNameList, checkFileSumList)
|
||||
if err != nil {
|
||||
res.Msg += fmt.Sprintf("### Repo File Check Failed:\n%s\n", err.Error())
|
||||
res.Failed = true
|
||||
}
|
||||
return
|
||||
}
|
|
@ -1,16 +1,14 @@
|
|||
package main
|
||||
package healthcheck
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/joint-online-judge/JOJ3/cmd/joj3/env"
|
||||
"github.com/joint-online-judge/JOJ3/internal/conf"
|
||||
)
|
||||
|
||||
|
@ -21,20 +19,9 @@ type CheckResult struct {
|
|||
TimePeriod int `json:"time_period"`
|
||||
}
|
||||
|
||||
func check(conf *conf.Conf) (checkResults []CheckResult, err error) {
|
||||
func runTeapot(conf *conf.Conf, actor, repoName string) (checkResults []CheckResult, err error) {
|
||||
os.Setenv("LOG_FILE_PATH", conf.Teapot.LogPath)
|
||||
os.Setenv("_TYPER_STANDARD_TRACEBACK", "1")
|
||||
actor := os.Getenv("GITHUB_ACTOR")
|
||||
repository := os.Getenv("GITHUB_REPOSITORY")
|
||||
if actor == "" ||
|
||||
repository == "" ||
|
||||
strings.Count(repository, "/") != 1 {
|
||||
slog.Error("teapot env not set")
|
||||
err = fmt.Errorf("teapot env not set")
|
||||
return
|
||||
}
|
||||
repoParts := strings.Split(env.Attr.Repository, "/")
|
||||
repoName := repoParts[1]
|
||||
var formattedGroups []string
|
||||
for _, group := range conf.Teapot.Groups {
|
||||
groupConfig := fmt.Sprintf("%s=%d:%d",
|
||||
|
@ -56,7 +43,7 @@ func check(conf *conf.Conf) (checkResults []CheckResult, err error) {
|
|||
slog.Error("teapot check exec", "error", err)
|
||||
return
|
||||
}
|
||||
if json.Unmarshal(stdoutBuf.Bytes(), &checkResults) != nil {
|
||||
if err = json.Unmarshal(stdoutBuf.Bytes(), &checkResults); err != nil {
|
||||
slog.Error("unmarshal teapot check result", "error", err,
|
||||
"stdout", stdoutBuf.String())
|
||||
return
|
||||
|
@ -86,73 +73,35 @@ func generateOutput(
|
|||
}
|
||||
}
|
||||
comment += fmt.Sprintf(
|
||||
"in last %d hour(s): submit count %d, max count %d\n",
|
||||
"in last %d hour(s): submit count %d, max count %d",
|
||||
checkResult.TimePeriod,
|
||||
checkResult.SubmitCount,
|
||||
checkResult.MaxCount,
|
||||
)
|
||||
if useGroup && checkResult.SubmitCount+1 > checkResult.MaxCount {
|
||||
err = fmt.Errorf("submit count exceeded")
|
||||
err = fmt.Errorf(
|
||||
"keyword `%s` submit count exceeded",
|
||||
checkResult.Name,
|
||||
)
|
||||
comment += ", exceeded"
|
||||
}
|
||||
comment += "\n"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func setupSlog() {
|
||||
opts := &slog.HandlerOptions{}
|
||||
handler := slog.NewTextHandler(os.Stderr, opts)
|
||||
logger := slog.New(handler)
|
||||
slog.SetDefault(logger)
|
||||
}
|
||||
|
||||
var (
|
||||
confPath string
|
||||
Version string = "debug"
|
||||
)
|
||||
|
||||
func mainImpl() (err error) {
|
||||
showVersion := flag.Bool("version", false, "print current version")
|
||||
flag.StringVar(&confPath, "conf-path", "./conf.json", "path for config file")
|
||||
flag.Parse()
|
||||
if *showVersion {
|
||||
fmt.Println(Version)
|
||||
return
|
||||
}
|
||||
setupSlog()
|
||||
slog.Info("start teapot-checker", "version", Version)
|
||||
commitMsg, err := conf.GetCommitMsg()
|
||||
if err != nil {
|
||||
slog.Error("get commit msg", "error", err)
|
||||
return
|
||||
}
|
||||
conventionalCommit, err := conf.ParseConventionalCommit(commitMsg)
|
||||
if err != nil {
|
||||
slog.Error("parse commit msg", "error", err)
|
||||
return
|
||||
}
|
||||
confObj, _, err := conf.ParseConfFile(confPath)
|
||||
if err != nil {
|
||||
slog.Error("parse conf", "error", err)
|
||||
return
|
||||
}
|
||||
groups := conf.MatchGroups(confObj, conventionalCommit)
|
||||
checkResults, err := check(confObj)
|
||||
func TeapotCheck(
|
||||
conf *conf.Conf, actor, repoName string, groups []string,
|
||||
) (output string, err error) {
|
||||
checkResults, err := runTeapot(conf, actor, repoName)
|
||||
if err != nil {
|
||||
slog.Error("teapot check", "error", err)
|
||||
return
|
||||
}
|
||||
output, err := generateOutput(checkResults, groups)
|
||||
output, err = generateOutput(checkResults, groups)
|
||||
if err != nil {
|
||||
slog.Error("generate output", "error", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(output)
|
||||
return
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := mainImpl(); err != nil {
|
||||
slog.Error("main exit", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user