feat: file check
squash all previous commits
This commit is contained in:
		
							parent
							
								
									52491478a4
								
							
						
					
					
						commit
						05876c290d
					
				
							
								
								
									
										28
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							|  | @ -38,6 +38,34 @@ | ||||||
| 	path = examples/keyword/clangtidy/sillycode | 	path = examples/keyword/clangtidy/sillycode | ||||||
| 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
| 	branch = keyword/clangtidy/sillycode | 	branch = keyword/clangtidy/sillycode | ||||||
|  | [submodule "examples/healthcheck/asciifile"] | ||||||
|  | 	path = examples/healthcheck/asciifile | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/asciifile | ||||||
|  | [submodule "examples/healthcheck/asciimsg"] | ||||||
|  | 	path = examples/healthcheck/asciimsg | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/asciimsg | ||||||
|  | [submodule "examples/healthcheck/forbiddenfile"] | ||||||
|  | 	path = examples/healthcheck/forbiddenfile | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/forbiddenfile | ||||||
|  | [submodule "examples/healthcheck/meta"] | ||||||
|  | 	path = examples/healthcheck/meta | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/meta | ||||||
|  | [submodule "examples/healthcheck/release"] | ||||||
|  | 	path = examples/healthcheck/release | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/release | ||||||
|  | [submodule "examples/healthcheck/reposize"] | ||||||
|  | 	path = examples/healthcheck/reposize | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/reposize | ||||||
|  | [submodule "examples/healthcheck/repoverify"] | ||||||
|  | 	path = examples/healthcheck/repoverify | ||||||
|  | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  | 	branch = healthcheck/repoverify | ||||||
| [submodule "examples/cppcheck/sillycode"] | [submodule "examples/cppcheck/sillycode"] | ||||||
| 	path = examples/cppcheck/sillycode | 	path = examples/cppcheck/sillycode | ||||||
| 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | 	url = ssh://git@focs.ji.sjtu.edu.cn:2222/FOCS-dev/JOJ3-examples.git | ||||||
|  |  | ||||||
|  | @ -84,3 +84,7 @@ Check the `Result` at <https://github.com/criyle/go-judge#rest-api-interface>. | ||||||
| 
 | 
 | ||||||
| -   `Score int`: score of the stage. | -   `Score int`: score of the stage. | ||||||
| -   `Comment string`: comment on the stage. | -   `Comment string`: comment on the stage. | ||||||
|  | 
 | ||||||
|  | ### HealthCheck | ||||||
|  | 
 | ||||||
|  | The repohealth check will return a json list to for check result. The structure of json file is in `pkg/healthcheck/util.go` | ||||||
|  |  | ||||||
							
								
								
									
										78
									
								
								cmd/healthcheck/main.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								cmd/healthcheck/main.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,78 @@ | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 
 | ||||||
|  | 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/healthcheck" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // parseMultiValueFlag parses a multi-value command-line flag and appends its values to the provided slice.
 | ||||||
|  | // It registers a flag with the specified name and description, associating it with a multiStringValue receiver.
 | ||||||
|  | func parseMultiValueFlag(values *[]string, flagName, description string) { | ||||||
|  | 	flag.Var((*multiStringValue)(values), flagName, description) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type multiStringValue []string | ||||||
|  | 
 | ||||||
|  | // Set appends a new value to the multiStringValue slice.
 | ||||||
|  | // It satisfies the flag.Value interface, allowing multiStringValue to be used as a flag value.
 | ||||||
|  | func (m *multiStringValue) Set(value string) error { | ||||||
|  | 	*m = append(*m, value) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *multiStringValue) String() string { | ||||||
|  | 	return fmt.Sprintf("%v", *m) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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", "", "") | ||||||
|  | 	releaseCategories := flag.String("releaseCategories", "", "") | ||||||
|  | 	releaseNumber := flag.Int("releaseNumber", 0, "") | ||||||
|  | 	// FIXME: for drone usage
 | ||||||
|  | 	adminDir := flag.String("admin", "", "") // adminDir is for config files
 | ||||||
|  | 	parseMultiValueFlag(&gitWhitelist, "whitelist", "") | ||||||
|  | 	parseMultiValueFlag(&metaFile, "meta", "") | ||||||
|  | 	parseMultiValueFlag(&releaseTags, "releaseTags", "") | ||||||
|  | 	flag.Parse() | ||||||
|  | 
 | ||||||
|  | 	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) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Fprint(os.Stderr, err) | ||||||
|  | 		os.Exit(1) | ||||||
|  | 	} | ||||||
|  | 	fmt.Printf("%s", jsonData) | ||||||
|  | } | ||||||
|  | @ -14,6 +14,18 @@ import ( | ||||||
| 
 | 
 | ||||||
| func compareStageResults(t *testing.T, actual, expected []stage.StageResult, regex bool) { | func compareStageResults(t *testing.T, actual, expected []stage.StageResult, regex bool) { | ||||||
| 	t.Helper() | 	t.Helper() | ||||||
|  | 
 | ||||||
|  | 	// For Test
 | ||||||
|  | 	fmt.Println("Actual:") | ||||||
|  | 	for _, result := range actual { | ||||||
|  | 		fmt.Println(result) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	fmt.Println("Expected:") | ||||||
|  | 	for _, result := range expected { | ||||||
|  | 		fmt.Println(result) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if len(actual) != len(expected) { | 	if len(actual) != len(expected) { | ||||||
| 		t.Fatalf("len(actual) = %d, expected %d", len(actual), len(expected)) | 		t.Fatalf("len(actual) = %d, expected %d", len(actual), len(expected)) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/asciifile
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/asciifile
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit 46804afd2ebe8787a9d43711b191e424134ce25b | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/asciimsg
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/asciimsg
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit e9ed6a464a507730ef956bb8ea6dbe84fffea7ad | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/forbiddenfile
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/forbiddenfile
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit 245d036af0cbfe3745ef303d239a2d7225067d0b | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/meta
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/meta
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit 4f5e444940d2c383a0b069405e2ef42b01878bc5 | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/release
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/release
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit a4cedea002a198c2dae373f7ee6f6dc67753fae6 | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/reposize
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/reposize
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit d78308bcaaaeda9ead86069652288ae911503e33 | ||||||
							
								
								
									
										1
									
								
								examples/healthcheck/repoverify
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
							
						
						
									
										1
									
								
								examples/healthcheck/repoverify
									
									
									
									
									
										Submodule
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | Subproject commit a7a3bd0894c2e727e3ab3f9ddda3d438fbb86b30 | ||||||
|  | @ -5,6 +5,7 @@ import ( | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cppcheck" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cppcheck" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/cpplint" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/diff" | ||||||
|  | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/healthcheck" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/keyword" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/keyword" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/resultstatus" | ||||||
| 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/sample" | 	_ "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/parsers/sample" | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								internal/parsers/healthcheck/meta.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								internal/parsers/healthcheck/meta.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
|  | 
 | ||||||
|  | var name = "healthcheck" | ||||||
|  | 
 | ||||||
|  | func init() { | ||||||
|  | 	stage.RegisterParser(name, &Healthcheck{}) | ||||||
|  | } | ||||||
							
								
								
									
										49
									
								
								internal/parsers/healthcheck/parser.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/parsers/healthcheck/parser.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,49 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	// "encoding/json"
 | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/internal/stage" | ||||||
|  | 	// "focs.ji.sjtu.edu.cn/git/FOCS-dev/JOJ3/pkg/healthcheck"
 | ||||||
|  | 	"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 { | ||||||
|  | 	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, | ||||||
|  | 			), | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return stage.ParserResult{ | ||||||
|  | 		Score:   0, | ||||||
|  | 		Comment: 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 | ||||||
|  | 	for _, result := range results { | ||||||
|  | 		res = append(res, Parse(result, *conf)) | ||||||
|  | 	} | ||||||
|  | 	return res, false, nil | ||||||
|  | } | ||||||
|  | @ -57,13 +57,13 @@ func (*Keyword) Run(results []stage.ExecutorResult, confAny any) ( | ||||||
| 		return nil, true, err | 		return nil, true, err | ||||||
| 	} | 	} | ||||||
| 	var res []stage.ParserResult | 	var res []stage.ParserResult | ||||||
| 	forceQuit := false | 	end := false | ||||||
| 	for _, result := range results { | 	for _, result := range results { | ||||||
| 		tmp, matched := Parse(result, *conf) | 		tmp, matched := Parse(result, *conf) | ||||||
| 		if matched && conf.EndOnMatch { | 		if matched && conf.EndOnMatch { | ||||||
| 			forceQuit = true | 			end = true | ||||||
| 		} | 		} | ||||||
| 		res = append(res, tmp) | 		res = append(res, tmp) | ||||||
| 	} | 	} | ||||||
| 	return res, forceQuit, nil | 	return res, end, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -19,12 +19,12 @@ func (*ResultStatus) Run(results []stage.ExecutorResult, confAny any) ( | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, true, err | 		return nil, true, err | ||||||
| 	} | 	} | ||||||
| 	forceQuit := false | 	end := false | ||||||
| 	var res []stage.ParserResult | 	var res []stage.ParserResult | ||||||
| 	for _, result := range results { | 	for _, result := range results { | ||||||
| 		comment := "" | 		comment := "" | ||||||
| 		if result.Status != stage.Status(envexec.StatusAccepted) { | 		if result.Status != stage.Status(envexec.StatusAccepted) { | ||||||
| 			forceQuit = true | 			end = true | ||||||
| 			comment = fmt.Sprintf( | 			comment = fmt.Sprintf( | ||||||
| 				"Unexpected executor status: %s.", result.Status, | 				"Unexpected executor status: %s.", result.Status, | ||||||
| 			) | 			) | ||||||
|  | @ -34,5 +34,5 @@ func (*ResultStatus) Run(results []stage.ExecutorResult, confAny any) ( | ||||||
| 			Comment: comment, | 			Comment: comment, | ||||||
| 		}) | 		}) | ||||||
| 	} | 	} | ||||||
| 	return res, forceQuit, nil | 	return res, end, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -166,5 +166,4 @@ type ParserResult struct { | ||||||
| type StageResult struct { | type StageResult struct { | ||||||
| 	Name    string         `json:"name"` | 	Name    string         `json:"name"` | ||||||
| 	Results []ParserResult `json:"results"` | 	Results []ParserResult `json:"results"` | ||||||
| 	ForceQuit bool           `json:"force_quit"` |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -26,7 +26,7 @@ func Run(stages []Stage) []StageResult { | ||||||
| 			slog.Error("parser not found", "name", stage.ParserName) | 			slog.Error("parser not found", "name", stage.ParserName) | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		parserResults, forceQuit, err := parser.Run(executorResults, stage.ParserConf) | 		parserResults, end, err := parser.Run(executorResults, stage.ParserConf) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			slog.Error("parser run error", "name", stage.ExecutorName, "error", err) | 			slog.Error("parser run error", "name", stage.ExecutorName, "error", err) | ||||||
| 			break | 			break | ||||||
|  | @ -35,9 +35,8 @@ func Run(stages []Stage) []StageResult { | ||||||
| 		stageResults = append(stageResults, StageResult{ | 		stageResults = append(stageResults, StageResult{ | ||||||
| 			Name:    stage.Name, | 			Name:    stage.Name, | ||||||
| 			Results: parserResults, | 			Results: parserResults, | ||||||
| 			ForceQuit: forceQuit, |  | ||||||
| 		}) | 		}) | ||||||
| 		if forceQuit { | 		if end { | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										83
									
								
								pkg/healthcheck/commit.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								pkg/healthcheck/commit.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"strings" | ||||||
|  | 	"unicode" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-git/go-git/v5" | ||||||
|  | 	"github.com/go-git/go-git/v5/plumbing/object" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // nonAsciiMsg checks for non-ASCII characters in the commit message.
 | ||||||
|  | // If the message starts with "Merge pull request", it skips the non-ASCII characters check.
 | ||||||
|  | // 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:   "", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// cmd := exec.Command("git", "log", "--encoding=UTF-8", "--format=%B")
 | ||||||
|  | 	repo, err := git.PlainOpen(root) | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = fmt.Errorf("Error openning git repo%w", err) | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ref, err := repo.Head() | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = fmt.Errorf("Error getting reference%w", err) | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 	commits, err := repo.Log(&git.LogOptions{From: ref.Hash()}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = fmt.Errorf("Error getting commits%w", err) | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var msgs []string | ||||||
|  | 	err = commits.ForEach(func(c *object.Commit) error { | ||||||
|  | 		msgs = append(msgs, c.Message) | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = fmt.Errorf("Error converting log to string%w", err) | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var nonAsciiMsgs []string | ||||||
|  | 	for _, msg := range msgs { | ||||||
|  | 		if msg == "" { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if strings.HasPrefix(msg, "Merge pull request") { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		for _, c := range msg { | ||||||
|  | 			if c > unicode.MaxASCII { | ||||||
|  | 				nonAsciiMsgs = append(nonAsciiMsgs, msg) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	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 | ||||||
|  | 	} | ||||||
|  | 	jsonOut.StdOut += "OK" | ||||||
|  | 	return jsonOut | ||||||
|  | } | ||||||
							
								
								
									
										94
									
								
								pkg/healthcheck/forbidden.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								pkg/healthcheck/forbidden.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"regexp" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // getForbiddens retrieves a list of forbidden files in the specified root directory.
 | ||||||
|  | // It searches for files that do not match the specified regex patterns in the given file list.
 | ||||||
|  | func getForbiddens(root string, fileList []string) ([]string, error) { | ||||||
|  | 	var matches []string | ||||||
|  | 
 | ||||||
|  | 	var regexList []*regexp.Regexp | ||||||
|  | 	regexList, err := getRegex(fileList) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Println("Error compiling regex:", err) | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = filepath.Walk(root, func(path string, info os.FileInfo, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if info.IsDir() { | ||||||
|  | 			if info.Name() == ".git" || info.Name() == ".gitea" || info.Name() == "ci" { | ||||||
|  | 				return filepath.SkipDir | ||||||
|  | 			} else { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		match := false | ||||||
|  | 		for _, regex := range regexList { | ||||||
|  | 			if regex.MatchString(info.Name()) { | ||||||
|  | 				match = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !match { | ||||||
|  | 			matches = append(matches, path) | ||||||
|  | 		} | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	return matches, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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"
 | ||||||
|  | 
 | ||||||
|  | 	forbids, err := getForbiddens(rootDir, regexList) | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = err | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var message string | ||||||
|  | 
 | ||||||
|  | 	if len(forbids) > 0 { | ||||||
|  | 		message += fmt.Sprint(103, "the following forbidden files were found: ") | ||||||
|  | 		for _, file := range forbids { | ||||||
|  | 			message += fmt.Sprint(file, ", ") | ||||||
|  | 		} | ||||||
|  | 		message += "\n\nTo fix it, first make a backup of your repository and then run the following commands:\nfor i in " | ||||||
|  | 		for _, file := range forbids { | ||||||
|  | 			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 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1 +0,0 @@ | ||||||
| package healthcheck |  | ||||||
							
								
								
									
										86
									
								
								pkg/healthcheck/meta.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								pkg/healthcheck/meta.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // getMetas retrieves a list of metadata files that are expected to exist in the specified root directory.
 | ||||||
|  | // It checks for the existence of each file in the fileList and provides instructions if any file is missing.
 | ||||||
|  | func getMetas(rootDir string, fileList []string) ([]string, string, error) { | ||||||
|  | 	addExt(fileList, "\\.*") | ||||||
|  | 	regexList, err := getRegex(fileList) | ||||||
|  | 	var unmatchedList []string | ||||||
|  | 
 | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, "", err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	files, err := os.ReadDir(rootDir) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, "", fmt.Errorf("Error reading directory:%w", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	matched := false | ||||||
|  | 	umatchedRes := "" | ||||||
|  | 
 | ||||||
|  | 	// TODO: it seems that there is no good find subsitution now
 | ||||||
|  | 	// modify current code if exist a better solution
 | ||||||
|  | 	for i, regex := range regexList { | ||||||
|  | 		for _, file := range files { | ||||||
|  | 			if file.IsDir() { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if regex.MatchString(file.Name()) { | ||||||
|  | 				matched = true | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 		if !matched { | ||||||
|  | 			unmatchedList = append(unmatchedList, fileList[i]) | ||||||
|  | 			str := fmt.Sprint("\tno ", fileList[i], " file found") | ||||||
|  | 			switch fileList[i] { | ||||||
|  | 			case "readme\\.*": | ||||||
|  | 				str += ", please refer to https://www.makeareadme.com/ for more information" | ||||||
|  | 			case "changelog\\.*": | ||||||
|  | 				str += ", please refer to https://keepachangelog.com/en/1.1.0/ for more information" | ||||||
|  | 			default: | ||||||
|  | 				str += "" | ||||||
|  | 			} | ||||||
|  | 			str += "\n" | ||||||
|  | 
 | ||||||
|  | 			umatchedRes += str | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return unmatchedList, umatchedRes, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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) { | ||||||
|  | 	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 | ||||||
|  | 		jsonOut.ErrorLog = err | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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 | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										90
									
								
								pkg/healthcheck/nonascii.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								pkg/healthcheck/nonascii.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,90 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"unicode" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // getNonAscii retrieves a list of files in the specified root directory that contain non-ASCII characters.
 | ||||||
|  | // It searches for non-ASCII characters in each file's content and returns a list of paths to files containing non-ASCII characters.
 | ||||||
|  | func getNonAscii(root string) ([]string, error) { | ||||||
|  | 	var nonAscii []string | ||||||
|  | 
 | ||||||
|  | 	err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if info.IsDir() { | ||||||
|  | 			if info.Name() == ".git" || info.Name() == ".gitea" || info.Name() == "ci" { | ||||||
|  | 				return filepath.SkipDir | ||||||
|  | 			} else { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if info.Name() == "healthcheck" { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		file, err := os.Open(path) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 		defer file.Close() | ||||||
|  | 
 | ||||||
|  | 		scanner := bufio.NewScanner(file) | ||||||
|  | 		for scanner.Scan() { | ||||||
|  | 			cont := true | ||||||
|  | 			for _, c := range scanner.Text() { | ||||||
|  | 				if c > unicode.MaxASCII { | ||||||
|  | 					nonAscii = append(nonAscii, "\t"+path) | ||||||
|  | 					cont = false | ||||||
|  | 					break | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			if !cont { | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	return nonAscii, err | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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:   "", | ||||||
|  | 	} | ||||||
|  | 	nonAscii, err := getNonAscii(root) | ||||||
|  | 	if err != nil { | ||||||
|  | 		jsonOut.StdOut += "Failed" | ||||||
|  | 		jsonOut.ExitCode = 1 | ||||||
|  | 		jsonOut.ErrorLog = err | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	jsonOut.StdErr = nonAsciiRes | ||||||
|  | 
 | ||||||
|  | 	return jsonOut | ||||||
|  | } | ||||||
							
								
								
									
										88
									
								
								pkg/healthcheck/release.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								pkg/healthcheck/release.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,88 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"log" | ||||||
|  | 
 | ||||||
|  | 	"github.com/go-git/go-git/v5" | ||||||
|  | 	"github.com/go-git/go-git/v5/plumbing" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func catTags(all []string) (out string) { | ||||||
|  | 	out = "" | ||||||
|  | 	for _, str := range all { | ||||||
|  | 		out += str + " " | ||||||
|  | 	} | ||||||
|  | 	return out | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func getTagsFromRepo(repoPath string) ([]string, error) { | ||||||
|  | 	repo, err := git.PlainOpen(repoPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Cannot open repo: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	refs, err := repo.Tags() | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Cannot get tags: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var tags []string | ||||||
|  | 	err = refs.ForEach(func(ref *plumbing.Reference) error { | ||||||
|  | 		tags = append(tags, ref.Name().Short()) | ||||||
|  | 		return nil | ||||||
|  | 	}) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("Error while 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:   "", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	tags, err := getTagsFromRepo(repoPath) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatalf("Error in getting tags: %v", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var prefix string | ||||||
|  | 
 | ||||||
|  | 	switch category { | ||||||
|  | 	case "exam": | ||||||
|  | 		prefix = "e" | ||||||
|  | 	case "project": | ||||||
|  | 		prefix = "p" | ||||||
|  | 	case "homework": | ||||||
|  | 		prefix = "h" | ||||||
|  | 	default: | ||||||
|  | 		prefix = "a" | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	target := prefix + fmt.Sprintf("%d", n) | ||||||
|  | 	found := false | ||||||
|  | 	for _, tag := range tags { | ||||||
|  | 		if tag == target { | ||||||
|  | 			found = true | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	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 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	jsonOut.StdOut += "OK" | ||||||
|  | 	jsonOut.ExitCode = 0 | ||||||
|  | 	jsonOut.StdErr = "Fine" | ||||||
|  | 	return jsonOut | ||||||
|  | } | ||||||
							
								
								
									
										56
									
								
								pkg/healthcheck/reposize.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								pkg/healthcheck/reposize.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"os/exec" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // 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:   "", | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// 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 | ||||||
|  | 		jsonOut.ErrorLog = fmt.Errorf("Error running git command:%w", err) | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	lines := strings.Split(string(output), "\n") | ||||||
|  | 	var sum int | ||||||
|  | 	for _, line := range lines { | ||||||
|  | 		if strings.Contains(line, "size") { | ||||||
|  | 			fields := strings.Fields(line) | ||||||
|  | 			sizeStr := fields[1] | ||||||
|  | 			size, err := strconv.Atoi(sizeStr) | ||||||
|  | 			if err != nil { | ||||||
|  | 				jsonOut.StdOut += "Failed" | ||||||
|  | 				jsonOut.ExitCode = 1 | ||||||
|  | 				jsonOut.ErrorLog = fmt.Errorf("Error running git command:%w", err) | ||||||
|  | 				return jsonOut | ||||||
|  | 			} | ||||||
|  | 			sum += size | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if sum <= 2048 { | ||||||
|  | 		jsonOut.StdOut += "OK" | ||||||
|  | 		return jsonOut | ||||||
|  | 	} | ||||||
|  | 	jsonOut.StdOut += "Failed" | ||||||
|  | 	jsonOut.ExitCode = 100 | ||||||
|  | 	jsonOut.StdErr = "repository larger than 2MB, please clean up or contact the teaching team." | ||||||
|  | 	return jsonOut | ||||||
|  | } | ||||||
							
								
								
									
										39
									
								
								pkg/healthcheck/utils.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								pkg/healthcheck/utils.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"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 of check"` | ||||||
|  | 	StdOut   string `json:"stdout"` | ||||||
|  | 	ExitCode int    `json:"exit code"` | ||||||
|  | 	StdErr   string `json:"stderr"` | ||||||
|  | 	ErrorLog error  `json:"errorLog"` | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // 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) { | ||||||
|  | 	for i, file := range fileList { | ||||||
|  | 		fileList[i] = file + ext | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // getRegex compiles each regex pattern in the fileList into a []*regexp.Regexp slice.
 | ||||||
|  | // It returns a slice containing compiled regular expressions.
 | ||||||
|  | func getRegex(fileList []string) ([]*regexp.Regexp, error) { | ||||||
|  | 	var regexList []*regexp.Regexp | ||||||
|  | 	for _, pattern := range fileList { | ||||||
|  | 		regex, err := regexp.Compile("(?i)" + pattern) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, fmt.Errorf("Error compiling regex:%w", err) | ||||||
|  | 		} | ||||||
|  | 		regexList = append(regexList, regex) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return regexList, nil | ||||||
|  | } | ||||||
							
								
								
									
										114
									
								
								pkg/healthcheck/verify.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								pkg/healthcheck/verify.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,114 @@ | ||||||
|  | package healthcheck | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"bufio" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"path/filepath" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // fileExists checks if a file exists at the specified path.
 | ||||||
|  | func fileExists(filePath string) bool { | ||||||
|  | 	_, err := os.Stat(filePath) | ||||||
|  | 	return !os.IsNotExist(err) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // filesMatch checks if two files are identical.
 | ||||||
|  | func filesMatch(file1, file2 string) (bool, error) { | ||||||
|  | 	f1, err := os.Open(file1) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	defer f1.Close() | ||||||
|  | 
 | ||||||
|  | 	f2, err := os.Open(file2) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return false, err | ||||||
|  | 	} | ||||||
|  | 	defer f2.Close() | ||||||
|  | 
 | ||||||
|  | 	scanner1 := bufio.NewScanner(f1) | ||||||
|  | 	scanner2 := bufio.NewScanner(f2) | ||||||
|  | 
 | ||||||
|  | 	for scanner1.Scan() && scanner2.Scan() { | ||||||
|  | 		line1 := scanner1.Text() | ||||||
|  | 		line2 := scanner2.Text() | ||||||
|  | 
 | ||||||
|  | 		if line1 != line2 { | ||||||
|  | 			return false, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if scanner1.Scan() || scanner2.Scan() { | ||||||
|  | 		// One file has more lines than the other
 | ||||||
|  | 		return false, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	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 { | ||||||
|  | 		if err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !info.IsDir() { | ||||||
|  | 			relPath, _ := filepath.Rel(dir1, path) | ||||||
|  | 			file2 := filepath.Join(dir2, 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 | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			// fmt.Printf("Checking integrity of %s:\n", path)
 | ||||||
|  | 			match, err := filesMatch(path, file2) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return 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 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 | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user