From 2d5fe25d1a5f92f97a9de44a9329ea347668f43d Mon Sep 17 00:00:00 2001 From: Boming Zhang Date: Sat, 15 Jun 2024 04:06:32 -0400 Subject: [PATCH] fix: output format --- cmd/healthcheck/main.go | 60 +++++++++++++------------- internal/parsers/healthcheck/parser.go | 26 +++++------ pkg/healthcheck/commit.go | 36 ++++------------ pkg/healthcheck/forbidden.go | 29 +++---------- pkg/healthcheck/meta.go | 28 +++--------- pkg/healthcheck/nonascii.go | 27 +++--------- pkg/healthcheck/release.go | 32 +++----------- pkg/healthcheck/reposize.go | 30 +++---------- pkg/healthcheck/utils.go | 9 ---- pkg/healthcheck/verify.go | 59 ++++--------------------- 10 files changed, 88 insertions(+), 248 deletions(-) diff --git a/cmd/healthcheck/main.go b/cmd/healthcheck/main.go index 2542a62..d10b311 100644 --- a/cmd/healthcheck/main.go +++ b/cmd/healthcheck/main.go @@ -1,7 +1,6 @@ package main import ( - "encoding/json" "flag" "fmt" "log/slog" @@ -38,10 +37,7 @@ func setupSlog() { // Generally, err is used for runtime errors, and checkRes is used for the result of the checks. func main() { - var info []healthcheck.CheckStage var gitWhitelist, metaFile, releaseTags []string - var tmp healthcheck.CheckStage - rootDir := flag.String("root", "", "") repo := flag.String("repo", "", "") droneBranch := flag.String("droneBranch", "", "") @@ -54,33 +50,35 @@ func main() { parseMultiValueFlag(&releaseTags, "releaseTags", "") flag.Parse() setupSlog() - tmp = healthcheck.RepoSize() - info = append(info, tmp) - - tmp = healthcheck.ForbiddenCheck(*rootDir, gitWhitelist, *repo, *droneBranch) - info = append(info, tmp) - - tmp = healthcheck.MetaCheck(*rootDir, metaFile) - info = append(info, tmp) - - tmp = healthcheck.NonAsciiFiles(*rootDir) - info = append(info, tmp) - - tmp = healthcheck.NonAsciiMsg(*rootDir) - info = append(info, tmp) - - // TODO: find a way to test the release tag - tmp = healthcheck.CheckReleases(*rootDir, *releaseCategories, *releaseNumber) - info = append(info, tmp) - - // FIXME: for drone usage - tmp = healthcheck.Verify(*rootDir, *adminDir) - info = append(info, tmp) - - jsonData, err := json.Marshal(info) + var err error + err = healthcheck.RepoSize() if err != nil { - fmt.Fprint(os.Stderr, err) - os.Exit(1) + fmt.Printf("## Repo Size Check Failed:\n%s\n", err.Error()) + } + err = healthcheck.ForbiddenCheck(*rootDir, gitWhitelist, *repo, *droneBranch) + if err != nil { + fmt.Printf("## Forbidden File Check Failed:\n%s\n", err.Error()) + } + err = healthcheck.MetaCheck(*rootDir, metaFile) + if err != nil { + fmt.Printf("## Forbidden 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()) + } + // TODO: find a way to test the release tag + err = healthcheck.CheckReleases(*rootDir, *releaseCategories, *releaseNumber) + if err != nil { + fmt.Printf("## Release Tag Check Failed:\n%s\n", err.Error()) + } + // FIXME: for drone usage + err = healthcheck.VerifyDirectory(*rootDir, *adminDir) + if err != nil { + fmt.Printf("## Directory File Check Failed:\n%s\n", err.Error()) } - fmt.Printf("%s", jsonData) } diff --git a/internal/parsers/healthcheck/parser.go b/internal/parsers/healthcheck/parser.go index 5655c3d..cae365c 100644 --- a/internal/parsers/healthcheck/parser.go +++ b/internal/parsers/healthcheck/parser.go @@ -7,41 +7,35 @@ import ( "github.com/criyle/go-judge/envexec" ) -type Conf struct { - Score int - Comment string -} - type Healthcheck struct{} -func Parse(executorResult stage.ExecutorResult, conf Conf) stage.ParserResult { +func Parse(executorResult stage.ExecutorResult) (stage.ParserResult, bool) { stdout := executorResult.Files["stdout"] stderr := executorResult.Files["stderr"] if executorResult.Status != stage.Status(envexec.StatusAccepted) { return stage.ParserResult{ Score: 0, Comment: fmt.Sprintf( - "Unexpected executor status: %s.\nStderr: %s", - executorResult.Status, stderr, + "Unexpected executor status: %s.\nStdout: %s\nStderr: %s", + executorResult.Status, stdout, stderr, ), - } + }, true } return stage.ParserResult{ Score: 0, Comment: stdout, - } + }, stdout != "" } func (*Healthcheck) Run(results []stage.ExecutorResult, confAny any) ( []stage.ParserResult, bool, error, ) { - conf, err := stage.DecodeConf[Conf](confAny) - if err != nil { - return nil, true, err - } var res []stage.ParserResult + forceQuit := false for _, result := range results { - res = append(res, Parse(result, *conf)) + parserResult, forceQuitResult := Parse(result) + res = append(res, parserResult) + forceQuit = forceQuit || forceQuitResult } - return res, false, nil + return res, forceQuit, nil } diff --git a/pkg/healthcheck/commit.go b/pkg/healthcheck/commit.go index 1532cc1..2db46d9 100644 --- a/pkg/healthcheck/commit.go +++ b/pkg/healthcheck/commit.go @@ -1,6 +1,7 @@ package healthcheck import ( + "fmt" "log/slog" "strings" "unicode" @@ -14,36 +15,23 @@ import ( // Otherwise, it iterates over each character in the message and checks if it is a non-ASCII character. // If a non-ASCII character is found, it returns an error indicating not to use non-ASCII characters in commit messages. // Otherwise, it returns nil indicating that the commit message is valid. -func NonAsciiMsg(root string) (jsonOut CheckStage) { - jsonOut = CheckStage{ - Name: "NonAsciiMsg", - StdOut: "Checking for non-ASCII characters in commit message: ", - ExitCode: 0, - StdErr: "", - } - +func NonAsciiMsg(root string) error { // cmd := exec.Command("git", "log", "--encoding=UTF-8", "--format=%B") repo, err := git.PlainOpen(root) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 slog.Error("openning git repo", "err", err) - return jsonOut + return fmt.Errorf("error openning git repo: %v", err) } ref, err := repo.Head() if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 slog.Error("getting reference", "err", err) - return jsonOut + return fmt.Errorf("error getting reference: %v", err) } commits, err := repo.Log(&git.LogOptions{From: ref.Hash()}) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 slog.Error("getting commits", "err", err) - return jsonOut + return fmt.Errorf("error getting commits: %v", err) } var msgs []string @@ -52,10 +40,8 @@ func NonAsciiMsg(root string) (jsonOut CheckStage) { return nil }) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 - slog.Error("converting log to string", "err", err) - return jsonOut + slog.Error("iterating commits", "err", err) + return fmt.Errorf("error iterating commits: %v", err) } var nonAsciiMsgs []string @@ -73,11 +59,7 @@ func NonAsciiMsg(root string) (jsonOut CheckStage) { } } if len(nonAsciiMsgs) > 0 { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 105 - jsonOut.StdErr = "Non-ASCII characters in commit messages:\n" + strings.Join(nonAsciiMsgs, "\n") - return jsonOut + return fmt.Errorf("Non-ASCII characters in commit messages:\n" + strings.Join(nonAsciiMsgs, "\n")) } - jsonOut.StdOut += "OK" - return jsonOut + return nil } diff --git a/pkg/healthcheck/forbidden.go b/pkg/healthcheck/forbidden.go index 9333094..042767f 100644 --- a/pkg/healthcheck/forbidden.go +++ b/pkg/healthcheck/forbidden.go @@ -16,7 +16,6 @@ func getForbiddens(root string, fileList []string) ([]string, error) { var regexList []*regexp.Regexp regexList, err := getRegex(fileList) if err != nil { - fmt.Println("Error compiling regex:", err) return nil, err } @@ -52,24 +51,11 @@ func getForbiddens(root string, fileList []string) ([]string, error) { // forbiddenCheck checks for forbidden files in the specified root directory. // It prints the list of forbidden files found, along with instructions on how to fix them. -func ForbiddenCheck(rootDir string, regexList []string, repo string, droneBranch string) (jsonOut CheckStage) { - jsonOut = CheckStage{ - Name: "forbiddenFile", - StdOut: "Checking forbidden files: ", - ExitCode: 0, - StdErr: "", - } - - // INFO: test case - // regexList := []string{`\.$`, `\.git`, `\.drone.yml`, `Makefile`, `CMakeLists.txt`,`.*\.go`,`.*\.toml`, `.*\.c`, `.*\.cc`, `.*\.cpp`, `.*\.h`, `.*\.md`} - // rootDir = "/Users/zhouzhaojiacheng/Desktop/STUDENT_ORG/TechJI/Dev/joj/JOJ3" - +func ForbiddenCheck(rootDir string, regexList []string, repo string, droneBranch string) error { forbids, err := getForbiddens(rootDir, regexList) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 - slog.Error("get forbiddens", "error", err) - return jsonOut + slog.Error("getting forbiddens", "error", err) + return fmt.Errorf("error getting forbiddens: %w", err) } var message string @@ -84,12 +70,7 @@ func ForbiddenCheck(rootDir string, regexList []string, repo string, droneBranch message += fmt.Sprint(file, " ") } message += fmt.Sprint("; do git filter-repo --force --invert-paths --path \\\"\\$i\\\"; done\ngit remote add origin ", repo, "\ngit push --set-upstream origin ", droneBranch, " --force") - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 103 - jsonOut.StdErr = message - return jsonOut - } else { - jsonOut.StdOut += "OK" - return jsonOut + return fmt.Errorf(message) } + return nil } diff --git a/pkg/healthcheck/meta.go b/pkg/healthcheck/meta.go index 3cad159..2e78d50 100644 --- a/pkg/healthcheck/meta.go +++ b/pkg/healthcheck/meta.go @@ -19,7 +19,7 @@ func getMetas(rootDir string, fileList []string) ([]string, string, error) { files, err := os.ReadDir(rootDir) if err != nil { - return nil, "", fmt.Errorf("Error reading directory:%w", err) + return nil, "", fmt.Errorf("error reading directory: %w", err) } matched := false @@ -60,28 +60,14 @@ func getMetas(rootDir string, fileList []string) ([]string, string, error) { // metaCheck performs a check for metadata files in the specified root directory. // It prints a message if any required metadata files are missing. -func MetaCheck(rootDir string, fileList []string) (jsonOut CheckStage) { +func MetaCheck(rootDir string, fileList []string) error { unmatchedList, umatchedRes, err := getMetas(rootDir, fileList) - jsonOut = CheckStage{ - Name: "metaFile", - StdOut: "Checking the existence of meta file: ", - ExitCode: 0, - StdErr: "", - } - if err != nil { - jsonOut.ExitCode = 1 - slog.Error("get metas", "err", err) - return jsonOut + slog.Error("getting metas", "err", err) + return fmt.Errorf("error getting metas: %w", err) } - - if len(unmatchedList) == 0 { - jsonOut.StdOut += "OK" - return jsonOut - } else { - jsonOut.ExitCode = 104 - jsonOut.StdOut += "Failed" - jsonOut.StdErr = fmt.Sprintf("%d important project files missing\n"+umatchedRes, len(unmatchedList)) - return jsonOut + if len(unmatchedList) != 0 { + return fmt.Errorf("%d important project files missing\n"+umatchedRes, len(unmatchedList)) } + return nil } diff --git a/pkg/healthcheck/nonascii.go b/pkg/healthcheck/nonascii.go index f2fe523..1d910f9 100644 --- a/pkg/healthcheck/nonascii.go +++ b/pkg/healthcheck/nonascii.go @@ -61,31 +61,14 @@ func getNonAscii(root string) ([]string, error) { // nonAsciiFiles checks for non-ASCII characters in files within the specified root directory. // It prints a message with the paths to files containing non-ASCII characters, if any. -func NonAsciiFiles(root string) (jsonOut CheckStage) { - jsonOut = CheckStage{ - Name: "NonAsciiFiles", - StdOut: "Checking for non-ascii files: ", - ExitCode: 0, - StdErr: "", - } +func NonAsciiFiles(root string) error { nonAscii, err := getNonAscii(root) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 - slog.Error("get non-ascii", "err", err) - return jsonOut + slog.Error("getting non-ascii", "err", err) + return fmt.Errorf("error getting non-ascii: %w", err) } - - var nonAsciiRes string if len(nonAscii) > 0 { - nonAsciiRes = fmt.Sprintf("Non-ASCII characters found in the following files:\n" + strings.Join(nonAscii, "\n")) - jsonOut.ExitCode = 105 - jsonOut.StdOut += "Failed" - } else { - jsonOut.StdOut += "OK" + return fmt.Errorf("Non-ASCII characters found in the following files:\n" + strings.Join(nonAscii, "\n")) } - - jsonOut.StdErr = nonAsciiRes - - return jsonOut + return nil } diff --git a/pkg/healthcheck/release.go b/pkg/healthcheck/release.go index 43e35e8..81beade 100644 --- a/pkg/healthcheck/release.go +++ b/pkg/healthcheck/release.go @@ -2,7 +2,6 @@ package healthcheck import ( "fmt" - "log" "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" @@ -19,12 +18,12 @@ func catTags(all []string) (out string) { func getTagsFromRepo(repoPath string) ([]string, error) { repo, err := git.PlainOpen(repoPath) if err != nil { - return nil, fmt.Errorf("Cannot open repo: %v", err) + return nil, fmt.Errorf("error opening repo: %v", err) } refs, err := repo.Tags() if err != nil { - return nil, fmt.Errorf("Cannot get tags: %v", err) + return nil, fmt.Errorf("error getting tags: %v", err) } var tags []string @@ -33,27 +32,18 @@ func getTagsFromRepo(repoPath string) ([]string, error) { return nil }) if err != nil { - return nil, fmt.Errorf("Error while iterating tags: %v", err) + return nil, fmt.Errorf("error iterating tags: %v", err) } return tags, nil } -func CheckReleases(repoPath string, category string, n int) (jsonOut CheckStage) { - jsonOut = CheckStage{ - Name: "ReleaseCheck", - StdOut: "Checking release tag: ", - ExitCode: 0, - StdErr: "", - } - +func CheckReleases(repoPath string, category string, n int) error { tags, err := getTagsFromRepo(repoPath) if err != nil { - log.Fatalf("Error in getting tags: %v", err) + return fmt.Errorf("error getting tags: %v", err) } - var prefix string - switch category { case "exam": prefix = "e" @@ -64,7 +54,6 @@ func CheckReleases(repoPath string, category string, n int) (jsonOut CheckStage) default: prefix = "a" } - target := prefix + fmt.Sprintf("%d", n) found := false for _, tag := range tags { @@ -75,14 +64,7 @@ func CheckReleases(repoPath string, category string, n int) (jsonOut CheckStage) } if !found { tagList := catTags(tags) - jsonOut.ExitCode = 107 - jsonOut.StdOut = "Failed" - jsonOut.StdErr = fmt.Sprintf("wrong release tag '%s', please use one of %s aborting", target, tagList) - return jsonOut + return fmt.Errorf("Wrong release tag '%s'. Please use one of %s.", target, tagList) } - - jsonOut.StdOut += "OK" - jsonOut.ExitCode = 0 - jsonOut.StdErr = "Fine" - return jsonOut + return nil } diff --git a/pkg/healthcheck/reposize.go b/pkg/healthcheck/reposize.go index 7103be4..dd17d06 100644 --- a/pkg/healthcheck/reposize.go +++ b/pkg/healthcheck/reposize.go @@ -1,6 +1,7 @@ package healthcheck import ( + "fmt" "log/slog" "os/exec" "strconv" @@ -9,25 +10,15 @@ import ( // RepoSize checks the size of the repository to determine if it is oversized. // It executes the 'git count-objects -v' command to obtain the size information, -func RepoSize() (jsonOut CheckStage) { - jsonOut = CheckStage{ - Name: "RepoSize", - StdOut: "Checking repository size: ", - ExitCode: 0, - StdErr: "", - } - +func RepoSize() error { // TODO: reimplement here when go-git is available // https://github.com/go-git/go-git/blob/master/COMPATIBILITY.md cmd := exec.Command("git", "count-objects", "-v") output, err := cmd.CombinedOutput() if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 slog.Error("running git command:", "err", err) - return jsonOut + return fmt.Errorf("error running git command: %w", err) } - lines := strings.Split(string(output), "\n") var sum int for _, line := range lines { @@ -36,21 +27,14 @@ func RepoSize() (jsonOut CheckStage) { sizeStr := fields[1] size, err := strconv.Atoi(sizeStr) if err != nil { - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 1 slog.Error("running git command:", "err", err) - return jsonOut + return fmt.Errorf("error running git command: %w", err) } sum += size } } - - if sum <= 2048 { - jsonOut.StdOut += "OK" - return jsonOut + if sum > 2048 { + return fmt.Errorf("Repository larger than 2MB. Please clean up or contact the teaching team.") } - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 100 - jsonOut.StdErr = "repository larger than 2MB, please clean up or contact the teaching team." - return jsonOut + return nil } diff --git a/pkg/healthcheck/utils.go b/pkg/healthcheck/utils.go index 449953c..8939f22 100644 --- a/pkg/healthcheck/utils.go +++ b/pkg/healthcheck/utils.go @@ -5,15 +5,6 @@ import ( "regexp" ) -// For ExitCode, see https://focs.ji.sjtu.edu.cn/git/TAs/resources/src/branch/drone/dronelib.checks -// 1 for unrecoverable error and 0 for succeses -type CheckStage struct { - Name string `json:"name"` - StdOut string `json:"stdout"` - ExitCode int `json:"exit code"` - StdErr string `json:"stderr"` -} - // addExt appends the specified extension to each file name in the given fileList. // It modifies the original fileList in place. func addExt(fileList []string, ext string) { diff --git a/pkg/healthcheck/verify.go b/pkg/healthcheck/verify.go index b201b53..d1595ce 100644 --- a/pkg/healthcheck/verify.go +++ b/pkg/healthcheck/verify.go @@ -47,68 +47,27 @@ func filesMatch(file1, file2 string) (bool, error) { return true, nil } -// compareDirectories compares the contents of two directories. -func compareDirectories(dir1, dir2 string, jsonOut *CheckStage) error { - allMatch := true - var message string - err := filepath.Walk(dir1, func(path string, info os.FileInfo, err error) error { +// VerifyDirectory checks if the contents of two directories are identical. +func VerifyDirectory(rootDir, compareDir string) error { + err := filepath.Walk(rootDir, func(path string, info os.FileInfo, err error) error { if err != nil { - return err + return fmt.Errorf("error walking directory: %w", err) } - if !info.IsDir() { - relPath, _ := filepath.Rel(dir1, path) - file2 := filepath.Join(dir2, relPath) - + relPath, _ := filepath.Rel(rootDir, path) + file2 := filepath.Join(compareDir, relPath) if !fileExists(file2) { - // fmt.Printf("File %s in %s is missing in %s\n, please immediately revert your changes!\n", relPath, dir1, dir2) - message += "File missing" - allMatch = false - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 101 - jsonOut.StdErr = message - return nil + return fmt.Errorf("File %s in %s is missing in %s. Please immediately revert your changes!\n", relPath, rootDir, compareDir) } - - // fmt.Printf("Checking integrity of %s:\n", path) match, err := filesMatch(path, file2) if err != nil { - return err + return fmt.Errorf("error matching files: %w", err) } - if !match { - // fmt.Printf("File %s in %s is not identical to %s\nPlease revert your changes or contact the teaching team if you have a valid reason for adjusting them.\n", relPath, dir1, dir2) - message += "File is not identical" - allMatch = false - jsonOut.StdOut += "Failed" - jsonOut.ExitCode = 101 - jsonOut.StdErr = message + return fmt.Errorf("File %s in %s is not identical to %s. Please revert your changes or contact the teaching team if you have a valid reason for adjusting them.\n", relPath, rootDir, compareDir) } } return nil }) - - if allMatch { - jsonOut.StdOut += "OK!" - } else { - jsonOut.StdOut += "Failed!" - } - return err } - -// Verify checks if the contents of two directories are identical. -func Verify(rootDir, compareDir string) CheckStage { - jsonOut := CheckStage{ - Name: "verifyFile", - StdOut: "Checking files to be verified: ", - ExitCode: 0, - StdErr: "", - } - err := compareDirectories(rootDir, compareDir, &jsonOut) - // fmt.Println("Comparison finished ") - if err != nil { - fmt.Println("Error:", err) - } - return jsonOut -}