1
0
forked from JOJ/JOJ3

Compare commits

...

8 Commits

Author SHA1 Message Date
404d2c6eb6
chore: use goproxy.cn [skip ci] 2026-04-26 22:01:19 -07:00
ba8b769a5b chore: update submodule references [skip ci] 2026-04-26 00:27:13 +00:00
04ae1c8674 feat: add whitelist char support to nonascii check (#100)
All checks were successful
submodules sync / sync (push) Successful in 4m27s
build / build (push) Successful in 6m32s
build / trigger-build-image (push) Successful in 23s
This commit brings support of whitelisted characters during repo healthcheck/non-ascii file check. Supported by an extra switch to `repo-health-checker`, `-whitelistedChars`. The argument takes a comma-separated list of non-ASCII characters and ignores them during repo healthcheck. Illegal cmdline input is logged by the logger.

Co-Authored-By: GitHub Copilot <noreply@microsoft.com>
<details>
<summary>Copilot Prompt</summary>
<br>
This is a repo for an online judge orchestrator system «JOJ3». Under `cmd/` lies a source directory for a Go command, `repo-health-checker`. You tell from its name that it checks the repo for stuff like repo size, commit message, non-ASCII character usage, etc. before sending the work to the actual judging and grading system.

Now, I want the non-ASCII character checking function of the repo health checker to be flexible - it shall accept a list of non-ASCII characters and deem them acceptable.

## Your task

- Accept this new cmdline arg. In `cmd/repo-health-checker/main.go`, accept a new command line flag `-whitelisted-chars`, which shall take exactly one string of comma-separated non-ASCII characters. This string shall be passed to the actual healthcheck package.
- Respect this list while scanning the files. In `pkg/healthcheck/nonascii.go`, function `getNonASCII()`, we utilize a bufio *Scanner* to scan through all files for non-ASCII characters. We would like the list of acceptable chars to be passed from the cmdline to here, and modify the scanner logic to actually accept the corresponding characters.
- Error handling and reporting. This command line arg, `-whitelisted-chars`, could be completely abscent; in which case, no characters shall be escaped by default. The comma-separated list passed to the command may contain ASCII characters or multiple characters that are not properly separated; in which case, ignore that element, and report the incident via the SLog logging framework used in this project.
- Test your work. Create new testcases under `examples/healthcheck/` to reflect this change. Reflect to `examples/healthcheck/asciifile/` to learn about how to configure the repo health checker. Integrate your work to the Go test framework such that it could be invoked by running `make test` at the terminal.
  - Note: Use `git init` to init your testcase directory and make a initial commit - this project, JOJ3, only runs in Git repos.

## Notes
- Directory structure. `cmd/` for invokable commands, `pkg/` for the actual logic, `internal` - something you don't need to worry about.
- JOJ3 vs. Health Check. `joj3` is a separate executable; in this session we are only working on the `repo-health-checker`.
- Extras. Make sure to read `README.md` and the directory structure before you go; also, create To-do before you execute your plan.
</details>

Reviewed-on: https://focs.ji.sjtu.edu.cn/git/JOJ/JOJ3/pulls/100
Reviewed-by: 张泊明518370910136 <bomingzh@sjtu.edu.cn>
Co-authored-by: Mack Wang <mac-wang@sjtu.edu.cn>
Co-committed-by: Mack Wang <mac-wang@sjtu.edu.cn>
2026-04-26 08:23:33 +08:00
9d75e359af feat: skip git module for non ascii check (#95)
Some checks failed
submodules sync / sync (push) Successful in 4m29s
build / build (push) Failing after 14m42s
build / trigger-build-image (push) Has been skipped
Skip git submodule non ascii check

check with by directly running `./build/repo-health-checker` at root after build

Co-authored-by: zzjc1234 <107383618+zzjc1234@users.noreply.github.com>
Reviewed-on: https://focs.ji.sjtu.edu.cn/git/JOJ/JOJ3/pulls/95
Reviewed-by: 张泊明518370910136 <bomingzh@sjtu.edu.cn>
Co-authored-by: 周赵嘉程521432910016 <zzjc123@sjtu.edu.cn>
Co-committed-by: 周赵嘉程521432910016 <zzjc123@sjtu.edu.cn>
2026-04-22 17:07:16 +08:00
64bc267300
refactor: remove backward compatibility fields
Some checks failed
submodules sync / sync (push) Successful in 2m4s
build / build (push) Waiting to run
build / trigger-build-image (push) Blocked by required conditions
build / build (pull_request) Failing after 13m25s
build / trigger-build-image (pull_request) Has been skipped
2026-03-22 03:02:21 -07:00
be12f79a4e
chore(deps): bump google.golang.org/grpc from v1.79.1 to v1.79.3
Some checks failed
submodules sync / sync (push) Successful in 3m39s
build / build (push) Failing after 10m1s
build / trigger-build-image (push) Has been skipped
2026-03-18 22:18:52 -07:00
bd15d5ce05
fix: groups recording for [all]
Some checks failed
submodules sync / sync (push) Successful in 4m20s
build / build (push) Failing after 12m42s
build / trigger-build-image (push) Has been skipped
2026-03-18 10:50:39 -07:00
e20ebfaa81 fix: check conf file ownership (#98)
All checks were successful
submodules sync / sync (push) Successful in 2m48s
build / build (push) Successful in 8m6s
build / trigger-build-image (push) Successful in 22s
Co-authored-by: arthurcai <arthurcai@sjtu.edu.cn>
Reviewed-on: https://focs.ji.sjtu.edu.cn/git/JOJ/JOJ3/pulls/98
Co-authored-by: Boming Zhang <bomingzh@sjtu.edu.cn>
Co-committed-by: Boming Zhang <bomingzh@sjtu.edu.cn>
2026-03-13 17:13:39 +08:00
12 changed files with 141 additions and 94 deletions

4
.gitmodules vendored
View File

@ -70,3 +70,7 @@
path = examples/cpplint/simple path = examples/cpplint/simple
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = cpplint/simple branch = cpplint/simple
[submodule "examples/healthcheck/whitelistedchars-success"]
path = examples/healthcheck/whitelistedchars-success
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/whitelistedchars-success

View File

@ -22,7 +22,7 @@ $ git clone ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3.git
2. Install [Go](https://go.dev/doc/install). Also, make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`. 2. Install [Go](https://go.dev/doc/install). Also, make sure `make` and `git` are installed and all 3 programs are presented in `$PATH`.
- If you have problem on connecting to the Go website and Go packages, download Go from [studygolang](https://studygolang.com/dl) and run `go env -w GOPROXY=https://goproxy.io,direct` to set the Go modules mirror proxy after installing Go. - If you have problem on connecting to the Go website and Go packages, download Go from [studygolang](https://studygolang.com/dl) and run `go env -w GOPROXY=https://goproxy.cn,direct` to set the Go modules mirror proxy after installing Go.
3. Enable cgroup v2 for your OS. For WSL2, check [here](https://stackoverflow.com/a/73376219/13724598). Also, enable linger for the user you used to run `go-judge` if you are using `systemd`, e.g. if the user is `go-judge`, run `loginctl enable-linger go-judge`. So that you do not need root permission to run `go-judge` (it can create a nesting cgroup in its user slice). 3. Enable cgroup v2 for your OS. For WSL2, check [here](https://stackoverflow.com/a/73376219/13724598). Also, enable linger for the user you used to run `go-judge` if you are using `systemd`, e.g. if the user is `go-judge`, run `loginctl enable-linger go-judge`. So that you do not need root permission to run `go-judge` (it can create a nesting cgroup in its user slice).

View File

@ -13,6 +13,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"syscall"
"github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5"
"github.com/koding/multiconfig" "github.com/koding/multiconfig"
@ -183,6 +184,17 @@ func GetConfPath(confRoot, confName, fallbackConfName, msg, tag string) (
return confPath, confStat, conventionalCommit, err return confPath, confStat, conventionalCommit, err
} }
} }
// Check file ownership
if stat, ok := confStat.Sys().(*syscall.Stat_t); ok {
uid := int(stat.Uid)
currentUID := os.Getuid()
if uid != currentUID {
err = fmt.Errorf("insecure configuration file: owned by uid %d, expected %d", uid, currentUID)
slog.Error("insecure conf file", "path", confPath, "uid", uid, "currentUID", currentUID)
return confPath, confStat, conventionalCommit, err
}
}
return confPath, confStat, conventionalCommit, err return confPath, confStat, conventionalCommit, err
} }
@ -190,32 +202,12 @@ func MatchGroups(conf *Conf, conventionalCommit *ConventionalCommit) []string {
seen := make(map[string]bool) seen := make(map[string]bool)
keywords := []string{} keywords := []string{}
loweredCommitGroup := strings.ToLower(conventionalCommit.Group) loweredCommitGroup := strings.ToLower(conventionalCommit.Group)
if loweredCommitGroup == "all" { matchAllGroups := loweredCommitGroup == "all"
for i := range conf.PreStages {
conf.PreStages[i].Group = ""
conf.PreStages[i].Groups = nil
}
for i := range conf.Stages {
conf.Stages[i].Group = ""
conf.Stages[i].Groups = nil
}
for i := range conf.PostStages {
conf.PostStages[i].Group = ""
conf.PostStages[i].Groups = nil
}
}
confStages := []ConfStage{} confStages := []ConfStage{}
confStages = append(confStages, conf.PreStages...) confStages = append(confStages, conf.PreStages...)
confStages = append(confStages, conf.Stages...) confStages = append(confStages, conf.Stages...)
confStages = append(confStages, conf.PostStages...) confStages = append(confStages, conf.PostStages...)
for _, stage := range confStages { for _, stage := range confStages {
if stage.Group != "" {
keyword := strings.ToLower(stage.Group)
if _, exists := seen[keyword]; !exists {
seen[keyword] = true
keywords = append(keywords, keyword)
}
}
if len(stage.Groups) > 0 { if len(stage.Groups) > 0 {
for _, group := range stage.Groups { for _, group := range stage.Groups {
keyword := strings.ToLower(group) keyword := strings.ToLower(group)
@ -229,10 +221,21 @@ func MatchGroups(conf *Conf, conventionalCommit *ConventionalCommit) []string {
slog.Info("group keywords from stages", "keywords", keywords) slog.Info("group keywords from stages", "keywords", keywords)
groups := []string{} groups := []string{}
for _, keyword := range keywords { for _, keyword := range keywords {
if strings.Contains(loweredCommitGroup, keyword) { if matchAllGroups || strings.Contains(loweredCommitGroup, keyword) {
groups = append(groups, keyword) groups = append(groups, keyword)
} }
} }
slog.Info("matched groups", "groups", groups) slog.Info("matched groups", "groups", groups)
if matchAllGroups {
for i := range conf.PreStages {
conf.PreStages[i].Groups = nil
}
for i := range conf.Stages {
conf.Stages[i].Groups = nil
}
for i := range conf.PostStages {
conf.PostStages[i].Groups = nil
}
}
return groups return groups
} }

View File

@ -6,7 +6,6 @@ import (
type ConfStage struct { type ConfStage struct {
Name string Name string
Group string // TODO: remove Group in the future
Groups []string Groups []string
Executor struct { Executor struct {
Name string Name string
@ -31,15 +30,6 @@ type Conf struct {
PreStages []ConfStage PreStages []ConfStage
Stages []ConfStage Stages []ConfStage
PostStages []ConfStage PostStages []ConfStage
// TODO: remove this nested struct
Stage struct {
SandboxExecServer string
SandboxToken string
OutputPath string
PreStages []ConfStage
Stages []ConfStage
PostStages []ConfStage
}
} }
type OptionalCmd struct { type OptionalCmd struct {

View File

@ -78,31 +78,6 @@ func loadConf(confPath string) (*joj3Conf.Conf, error) {
slog.Error("parse conf", "error", err) slog.Error("parse conf", "error", err)
return nil, err return nil, err
} }
// TODO: remove this compatible code for nested struct
if conf.Stage.SandboxExecServer != "" {
conf.SandboxExecServer = conf.Stage.SandboxExecServer
conf.Stage.SandboxExecServer = ""
}
if conf.Stage.SandboxToken != "" {
conf.SandboxToken = conf.Stage.SandboxToken
conf.Stage.SandboxToken = ""
}
if conf.Stage.OutputPath != "" {
conf.OutputPath = conf.Stage.OutputPath
conf.Stage.OutputPath = ""
}
if len(conf.Stage.PreStages) > 0 {
conf.PreStages = conf.Stage.PreStages
conf.Stage.PreStages = nil
}
if len(conf.Stage.Stages) > 0 {
conf.Stages = conf.Stage.Stages
conf.Stage.Stages = nil
}
if len(conf.Stage.PostStages) > 0 {
conf.PostStages = conf.Stage.PostStages
conf.Stage.PostStages = nil
}
slog.Debug("conf loaded", "conf", conf, "joj3 version", Version) slog.Debug("conf loaded", "conf", conf, "joj3 version", Version)
return conf, nil return conf, nil
} }

View File

@ -44,21 +44,13 @@ func generateStages(confStages []conf.ConfStage, groups []string) (
stages := []stage.Stage{} stages := []stage.Stage{}
existNames := map[string]bool{} existNames := map[string]bool{}
for i, s := range confStages { for i, s := range confStages {
if len(groups) == 0 && (len(s.Groups) != 0 || s.Group != "") { if len(groups) == 0 && (len(s.Groups) != 0) {
continue continue
} }
ok := false ok := false
if s.Group == "" && len(s.Groups) == 0 { if len(s.Groups) == 0 {
ok = true ok = true
} }
if !ok && s.Group != "" {
for _, group := range groups {
if strings.EqualFold(group, s.Group) {
ok = true
break
}
}
}
if !ok && len(s.Groups) > 0 { if !ok && len(s.Groups) > 0 {
for _, group := range groups { for _, group := range groups {
for _, g := range s.Groups { for _, g := range s.Groups {

View File

@ -45,6 +45,7 @@ var (
checkFileNameList string checkFileNameList string
checkFileSumList string checkFileSumList string
metaFile []string metaFile []string
whitelistedChars string
allowedDomainList string allowedDomainList string
actorCsvPath string actorCsvPath string
showVersion *bool showVersion *bool
@ -57,6 +58,7 @@ func init() {
flag.Float64Var(&repoSize, "repoSize", 2, "maximum size of the repo in MiB") flag.Float64Var(&repoSize, "repoSize", 2, "maximum size of the repo in MiB")
flag.StringVar(&checkFileNameList, "checkFileNameList", "", "comma-separated list of files to check") flag.StringVar(&checkFileNameList, "checkFileNameList", "", "comma-separated list of files to check")
flag.StringVar(&checkFileSumList, "checkFileSumList", "", "comma-separated list of expected checksums") flag.StringVar(&checkFileSumList, "checkFileSumList", "", "comma-separated list of expected checksums")
flag.StringVar(&whitelistedChars, "whitelistedChars", "", "comma-separated list of non-ASCII characters allowed in files")
flag.StringVar(&allowedDomainList, "allowedDomainList", "sjtu.edu.cn", "comma-separated list of allowed domains for commit author email") flag.StringVar(&allowedDomainList, "allowedDomainList", "sjtu.edu.cn", "comma-separated list of allowed domains for commit author email")
flag.StringVar(&actorCsvPath, "actorCsvPath", "/home/tt/.config/joj/students.csv", "path to actor csv file") flag.StringVar(&actorCsvPath, "actorCsvPath", "/home/tt/.config/joj/students.csv", "path to actor csv file")
parseMultiValueFlag(&metaFile, "meta", "meta files to check") parseMultiValueFlag(&metaFile, "meta", "meta files to check")
@ -74,12 +76,14 @@ func main() {
"repoSize", repoSize, "repoSize", repoSize,
"checkFileNameList", checkFileNameList, "checkFileNameList", checkFileNameList,
"checkFileSumList", checkFileSumList, "checkFileSumList", checkFileSumList,
"whitelistedChars", whitelistedChars,
"meta", metaFile, "meta", metaFile,
) )
res := healthcheck.All( res := healthcheck.All(
rootDir, rootDir,
checkFileNameList, checkFileNameList,
checkFileSumList, checkFileSumList,
whitelistedChars,
allowedDomainList, allowedDomainList,
actorCsvPath, actorCsvPath,
metaFile, metaFile,

@ -0,0 +1 @@
Subproject commit 08aaaf1c3ab5863cc53475d8cbca0d668d6a8a1d

2
go.mod
View File

@ -12,7 +12,7 @@ require (
github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7 github.com/koding/multiconfig v0.0.0-20171124222453-69c27309b2d7
github.com/mcuadros/go-defaults v1.2.0 github.com/mcuadros/go-defaults v1.2.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
google.golang.org/grpc v1.79.1 google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11 google.golang.org/protobuf v1.36.11
) )

25
go.sum
View File

@ -11,6 +11,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/criyle/go-judge/pb v1.3.2 h1:S0c0EqRF+xePOwcZxSb9mPV+bkXgfOX9f7SQMrcdeb4= github.com/criyle/go-judge/pb v1.3.2 h1:S0c0EqRF+xePOwcZxSb9mPV+bkXgfOX9f7SQMrcdeb4=
@ -38,8 +40,6 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s= github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M= github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@ -102,21 +102,16 @@ github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
@ -144,10 +139,8 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA= google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2 h1:2I6GHUeJ/4shcDpoUlLs/2WPnhg7yJwvXtqcMJt9liA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251213004720-97cd9d5aeac2/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc v1.79.1 h1:zGhSi45ODB9/p3VAawt9a+O/MULLl9dpizzNNpq7flY=
google.golang.org/grpc v1.79.1/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -12,7 +12,8 @@ type Result struct {
} }
func All( func All(
rootDir, checkFileNameList, checkFileSumList, allowedDomainList, actorCsvPath string, rootDir, checkFileNameList, checkFileSumList, whitelistedChars,
allowedDomainList, actorCsvPath string,
metaFile []string, repoSize float64, metaFile []string, repoSize float64,
) (res Result) { ) (res Result) {
var err error var err error
@ -44,7 +45,7 @@ func All(
} else { } else {
res.Msg += "### Meta File Check Passed\n" res.Msg += "### Meta File Check Passed\n"
} }
err = NonASCIIFiles(rootDir) err = NonASCIIFiles(rootDir, whitelistedChars)
if err != nil { if err != nil {
res.Msg += fmt.Sprintf("### Non-ASCII Characters File Check Failed:\n%s\n", err.Error()) res.Msg += fmt.Sprintf("### Non-ASCII Characters File Check Failed:\n%s\n", err.Error())
res.Failed = true res.Failed = true

View File

@ -8,13 +8,83 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
"unicode" "unicode"
"unicode/utf8"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing/format/gitattributes" "github.com/go-git/go-git/v5/plumbing/format/gitattributes"
) )
// Read the list of comma-separated allowed characters from command line and convert it to a hashmap.
func parseWhitelistedChars(csv string) map[rune]struct{} {
whitelist := make(map[rune]struct{})
if strings.TrimSpace(csv) == "" {
return whitelist
}
for _, raw := range strings.Split(csv, ",") {
elem := strings.TrimSpace(raw)
if elem == "" {
slog.Warn("ignoring invalid whitelisted-chars element", "element", raw, "reason", "empty element")
continue
}
if utf8.RuneCountInString(elem) != 1 {
slog.Warn("ignoring invalid whitelisted-chars element", "element", elem, "reason", "element must be exactly one character")
continue
}
ch, _ := utf8.DecodeRuneInString(elem)
if ch == utf8.RuneError {
slog.Warn("ignoring invalid whitelisted-chars element", "element", elem, "reason", "invalid utf-8 rune")
continue
}
if ch <= unicode.MaxASCII {
slog.Warn("ignoring invalid whitelisted-chars element", "element", elem, "reason", "ASCII characters are not allowed")
continue
}
whitelist[ch] = struct{}{}
}
return whitelist
}
// getSubmodulePathsFromGoGit uses the go-git library to open the repository
// at the given root path and retrieve a list of all submodule paths.
// It returns a set of submodule paths for efficient lookup.
func getSubmodulePathsFromGoGit(root string) (map[string]struct{}, error) {
submodulePaths := make(map[string]struct{})
// Open the git repository at the given path.
repo, err := git.PlainOpen(root)
if err != nil {
if err == git.ErrRepositoryNotExists {
return submodulePaths, nil
}
return nil, fmt.Errorf("error opening git repository: %w", err)
}
worktree, err := repo.Worktree()
if err != nil {
return nil, fmt.Errorf("error getting worktree: %w", err)
}
// Get the list of submodules.
submodules, err := worktree.Submodules()
if err != nil {
return nil, fmt.Errorf("error getting submodules: %w", err)
}
for _, sm := range submodules {
submodulePaths[filepath.ToSlash(sm.Config().Path)] = struct{}{}
}
return submodulePaths, nil
}
// getNonASCII retrieves a list of files in the specified root directory that contain non-ASCII characters. // 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. // 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) { func getNonASCII(root string, whitelist map[rune]struct{}) ([]string, error) {
var nonASCII []string var nonASCII []string
gitattrExist := true gitattrExist := true
var matcher gitattributes.Matcher var matcher gitattributes.Matcher
@ -23,6 +93,11 @@ func getNonASCII(root string) ([]string, error) {
gitattrExist = false gitattrExist = false
} }
submodules, err := getSubmodulePathsFromGoGit(root)
if err != nil {
return nil, err
}
if gitattrExist { if gitattrExist {
fs := os.DirFS(".") fs := os.DirFS(".")
f, err := fs.Open(".gitattributes") f, err := fs.Open(".gitattributes")
@ -42,18 +117,22 @@ func getNonASCII(root string) ([]string, error) {
return err return err
} }
relPath, err := filepath.Rel(root, path)
if err != nil {
return err
}
if info.IsDir() { if info.IsDir() {
if info.Name() == ".git" { if info.Name() == ".git" {
return filepath.SkipDir return filepath.SkipDir
} }
if _, isSubmodule := submodules[relPath]; isSubmodule {
return filepath.SkipDir
}
return nil return nil
} }
if gitattrExist { if gitattrExist {
relPath, err := filepath.Rel(root, path)
if err != nil {
return err
}
ret, matched := matcher.Match(strings.Split(relPath, "/"), nil) ret, matched := matcher.Match(strings.Split(relPath, "/"), nil)
if matched && ret["text"].IsUnset() && !ret["text"].IsSet() { if matched && ret["text"].IsUnset() && !ret["text"].IsSet() {
return nil return nil
@ -70,6 +149,9 @@ func getNonASCII(root string) ([]string, error) {
for scanner.Scan() { for scanner.Scan() {
cont := true cont := true
for _, c := range scanner.Text() { for _, c := range scanner.Text() {
if _, ok := whitelist[c]; ok {
continue
}
if c > unicode.MaxASCII { if c > unicode.MaxASCII {
nonASCII = append(nonASCII, "\t"+path) nonASCII = append(nonASCII, "\t"+path)
cont = false cont = false
@ -89,8 +171,10 @@ func getNonASCII(root string) ([]string, error) {
// NonASCIIFiles checks for non-ASCII characters in files within the specified root directory. // 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. // It prints a message with the paths to files containing non-ASCII characters, if any.
func NonASCIIFiles(root string) error { // Additionally it accept a list of whitelisted characters that are allowed, repo-wide.
nonASCII, err := getNonASCII(root) func NonASCIIFiles(root, whitelistedChars string) error {
whitelist := parseWhitelistedChars(whitelistedChars)
nonASCII, err := getNonASCII(root, whitelist)
if err != nil { if err != nil {
slog.Error("getting non-ascii", "err", err) slog.Error("getting non-ascii", "err", err)
return fmt.Errorf("error getting non-ascii: %w", err) return fmt.Errorf("error getting non-ascii: %w", err)