Compare commits

..

1 Commits

35 changed files with 171 additions and 443 deletions

View File

@ -17,8 +17,10 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub
ssh -o StrictHostKeyChecking=accept-new -T git@focs.gc.sjtu.edu.cn -p 2222
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519.pub
ssh-keyscan -p 2222 focs.ji.sjtu.edu.cn >> ~/.ssh/known_hosts
ssh -T git@focs.ji.sjtu.edu.cn -p 2222
# - name: Lint
# run: make lint
- name: Build
@ -39,16 +41,18 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub
ssh -o StrictHostKeyChecking=accept-new -T git@focs.gc.sjtu.edu.cn -p 2222
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519.pub
ssh-keyscan -p 2222 focs.ji.sjtu.edu.cn >> ~/.ssh/known_hosts
ssh -T git@focs.ji.sjtu.edu.cn -p 2222
- name: Set up Git
run: |
git config --global user.name "bot-joj"
git config --global user.email "bot-joj@focs.gc.sjtu.edu.cn"
git config --global user.email "bot-joj@focs.ji.sjtu.edu.cn"
- name: Clone, Commit and Push
shell: bash
run: |
git clone ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/runner-images.git runner-images
git clone ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/runner-images.git runner-images
cd runner-images
git submodule update --init --remote --force
git add -A

View File

@ -16,13 +16,15 @@ jobs:
mkdir -p ~/.ssh
echo "${{ secrets.DEPLOY_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
echo "${{ secrets.DEPLOY_KEY }}" > ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/id_ed25519 ~/.ssh/id_ed25519.pub
ssh -o StrictHostKeyChecking=accept-new -T git@focs.gc.sjtu.edu.cn -p 2222
chmod 600 ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519.pub
ssh-keyscan -p 2222 focs.ji.sjtu.edu.cn >> ~/.ssh/known_hosts
ssh -T git@focs.ji.sjtu.edu.cn -p 2222
- name: Set up Git
run: |
git config --global user.name "bot-joj"
git config --global user.email "bot-joj@focs.gc.sjtu.edu.cn"
git remote set-url origin ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3.git
git config --global user.email "bot-joj@focs.ji.sjtu.edu.cn"
git remote set-url origin ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3.git
- name: Git Submodule Update
run: |
git submodule init

40
.gitmodules vendored
View File

@ -1,76 +1,72 @@
[submodule "examples/diff/basic"]
path = examples/diff/basic
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = diff/basic
[submodule "examples/diff/complex"]
path = examples/diff/complex
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = diff/complex
[submodule "examples/compile/success"]
path = examples/compile/success
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = compile/success
[submodule "examples/compile/error"]
path = examples/compile/error
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = compile/error
[submodule "examples/sample/success"]
path = examples/sample/success
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = sample/success
[submodule "examples/sample/error"]
path = examples/sample/error
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = sample/error
[submodule "examples/keyword/cpplint/sillycode"]
path = examples/keyword/cpplint/sillycode
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = keyword/cpplint/sillycode
[submodule "examples/keyword/clangtidy/sillycode"]
path = examples/keyword/clangtidy/sillycode
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = keyword/clangtidy/sillycode
[submodule "examples/healthcheck/asciifile"]
path = examples/healthcheck/asciifile
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/asciifile
[submodule "examples/healthcheck/asciimsg"]
path = examples/healthcheck/asciimsg
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/asciimsg
[submodule "examples/healthcheck/forbiddenfile"]
path = examples/healthcheck/forbiddenfile
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/forbiddenfile
[submodule "examples/healthcheck/meta"]
path = examples/healthcheck/meta
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/meta
[submodule "examples/healthcheck/release"]
path = examples/healthcheck/release
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/release
[submodule "examples/healthcheck/reposize"]
path = examples/healthcheck/reposize
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/reposize
[submodule "examples/healthcheck/repoverify"]
path = examples/healthcheck/repoverify
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/repoverify
[submodule "examples/keyword/script"]
path = examples/keyword/script
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = keyword/script
[submodule "examples/cppcheck/simple"]
path = examples/cppcheck/simple
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = cppcheck/simple
[submodule "examples/cpplint/simple"]
path = examples/cpplint/simple
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
url = ssh://git@focs.ji.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = cpplint/simple
[submodule "examples/healthcheck/whitelistedchars-success"]
path = examples/healthcheck/whitelistedchars-success
url = ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3-examples.git
branch = healthcheck/whitelistedchars-success

View File

@ -17,12 +17,12 @@
1. Clone this repo in a Linux computer. For Windows, use [WSL 2](https://learn.microsoft.com/en-us/windows/wsl/install).
```bash
$ git clone ssh://git@focs.gc.sjtu.edu.cn:2222/JOJ/JOJ3.git
$ 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`.
- 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.
- 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.
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).
@ -85,7 +85,7 @@ pre-commit installed at .git/hooks/pre-commit
## Workflow
These steps are executed within [runner-images](https://focs.gc.sjtu.edu.cn/git/JOJ/runner-images), as specified in the YAML files under `.gitea/workflows` in student repositories. Our customized [`act_runner`](https://github.com/focs-gitea/act_runner) ensures that only labeled images controlled by administrators can be used. Furthermore, our private Docker registry requires authentication for pushing images, so only administrator-created images can be used to run `joj3`.
These steps are executed within [runner-images](https://focs.ji.sjtu.edu.cn/git/JOJ/runner-images), as specified in the YAML files under `.gitea/workflows` in student repositories. Our customized [`act_runner`](https://github.com/focs-gitea/act_runner) ensures that only labeled images controlled by administrators can be used. Furthermore, our private Docker registry requires authentication for pushing images, so only administrator-created images can be used to run `joj3`.
Inside the container created by `act_runner`, we use `sudo -E -u tt` to elevate permissions and run `joj3` with environment variables provided by Gitea Actions. All sensitive files should be stored on the host machine under the `tt` user's directory and mounted into the runner (e.g., `/home/tt/.config`). Allowed mount directories are also limited in `act_runner` configuration. Since the default `student` user inside the container (created from runner-images) shares the same UID as the `student` user on the host, which does not have the permission to access `tt`'s files. This helps ensure data security.

View File

@ -187,12 +187,14 @@ func GetConfPath(confRoot, confName, fallbackConfName, msg, tag string) (
// 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)
currentUid := os.Getuid()
if uid != 0 && uid != currentUid {
err = fmt.Errorf("insecure configuration file: owned by uid %d, expected 0 or %d", uid, currentUid)
slog.Error("insecure conf file", "path", confPath, "uid", uid, "expected_uid", currentUid)
return confPath, confStat, conventionalCommit, err
}
} else {
slog.Warn("could not determine file ownership, proceeding with caution", "path", confPath)
}
return confPath, confStat, conventionalCommit, err
@ -202,12 +204,32 @@ func MatchGroups(conf *Conf, conventionalCommit *ConventionalCommit) []string {
seen := make(map[string]bool)
keywords := []string{}
loweredCommitGroup := strings.ToLower(conventionalCommit.Group)
matchAllGroups := loweredCommitGroup == "all"
if 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 = append(confStages, conf.PreStages...)
confStages = append(confStages, conf.Stages...)
confStages = append(confStages, conf.PostStages...)
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 {
for _, group := range stage.Groups {
keyword := strings.ToLower(group)
@ -221,21 +243,10 @@ func MatchGroups(conf *Conf, conventionalCommit *ConventionalCommit) []string {
slog.Info("group keywords from stages", "keywords", keywords)
groups := []string{}
for _, keyword := range keywords {
if matchAllGroups || strings.Contains(loweredCommitGroup, keyword) {
if strings.Contains(loweredCommitGroup, keyword) {
groups = append(groups, keyword)
}
}
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
}

View File

@ -93,24 +93,24 @@ func TestParseConventionalCommit(t *testing.T) {
},
{
name: "Multi-line body",
commit: "feat(h1/e2): group (#86)\n\nReviewed-on: https://focs.gc.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>\n",
commit: "feat(h1/e2): group (#86)\n\nReviewed-on: https://focs.ji.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>\n",
want: &ConventionalCommit{
Type: "feat",
Scope: "h1/e2",
Description: "group (#86)",
Body: "Reviewed-on: https://focs.gc.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>",
Body: "Reviewed-on: https://focs.ji.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>",
Footer: "",
},
wantErr: false,
},
{
name: "Multi-line body with footer",
commit: "feat(h1/e2): group (#86)\n\nReviewed-on: https://focs.gc.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>\n\nFooter here\n",
commit: "feat(h1/e2): group (#86)\n\nReviewed-on: https://focs.ji.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>\n\nFooter here\n",
want: &ConventionalCommit{
Type: "feat",
Scope: "h1/e2",
Description: "group (#86)",
Body: "Reviewed-on: https://focs.gc.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>",
Body: "Reviewed-on: https://focs.ji.sjtu.edu.cn/git/test/test/pulls/86\nReviewed-by: foo <foo@sjtu.edu.cn>\nReviewed-by: bar <bar@sjtu.edu.cn>\nReviewed-by: nobody <nobody@sjtu.edu.cn>",
Footer: "Footer here",
},
wantErr: false,

View File

@ -6,6 +6,7 @@ import (
type ConfStage struct {
Name string
Group string // TODO: remove Group in the future
Groups []string
Executor struct {
Name string
@ -30,6 +31,15 @@ type Conf struct {
PreStages []ConfStage
Stages []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 {

View File

@ -78,6 +78,31 @@ func loadConf(confPath string) (*joj3Conf.Conf, error) {
slog.Error("parse conf", "error", 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)
return conf, nil
}

View File

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

View File

@ -45,7 +45,6 @@ var (
checkFileNameList string
checkFileSumList string
metaFile []string
whitelistedChars string
allowedDomainList string
actorCsvPath string
showVersion *bool
@ -58,7 +57,6 @@ func init() {
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(&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(&actorCsvPath, "actorCsvPath", "/home/tt/.config/joj/students.csv", "path to actor csv file")
parseMultiValueFlag(&metaFile, "meta", "meta files to check")
@ -76,14 +74,12 @@ func main() {
"repoSize", repoSize,
"checkFileNameList", checkFileNameList,
"checkFileSumList", checkFileSumList,
"whitelistedChars", whitelistedChars,
"meta", metaFile,
)
res := healthcheck.All(
rootDir,
checkFileNameList,
checkFileSumList,
whitelistedChars,
allowedDomainList,
actorCsvPath,
metaFile,

@ -1 +1 @@
Subproject commit 49880ce1a9aa676fc3d82f9cd4a733637006cc60
Subproject commit 066cb7cd75bdd2487877a5a23562ca1ebb1146ee

@ -1 +1 @@
Subproject commit 013a3b4fbaed128278d129b68e8f043fcd1d2d6a
Subproject commit a90e4ae896fedd70b959c0e99bdfa0450ae02490

@ -1 +1 @@
Subproject commit 581f22c24a4bb818c87f6fcf832de36fd0e8eb2a
Subproject commit 3ccb9b4cf063b3e05cf4ffc9f3872cbb6d5d8a43

@ -1 +1 @@
Subproject commit 1eb3926dd54e01246c0b9d05b0dd3c9bcdb53699
Subproject commit 0972415c0eddb24227c47f0ae3bdb567def34ee5

@ -1 +1 @@
Subproject commit 5f619d53abe4177d3dd5005350a0e8033881ffff
Subproject commit 0255cace13008943548f0ab8753ced388a98de11

@ -1 +1 @@
Subproject commit eab572c3668b700670cc3b53e64a925860cbea38
Subproject commit fcd3d8b589d03387e804efd39c1c7a3c24208c90

@ -1 +1 @@
Subproject commit 4ac3342ccfd386af01d1ef79542fa336650b6557
Subproject commit 395a1bfdfe52d2935044fd5d00effdc4c0569d37

@ -1 +1 @@
Subproject commit bd771db9e0f39dd60dd3f17d89f62377135b7a34
Subproject commit 4ce9726296bde680da24f1ec2718810a78c18547

@ -1 +1 @@
Subproject commit 90345a0463ab47e101d8e8d9c97ec0422736ad6b
Subproject commit 0e1860f9d0e039fd3ab891339cd01d72d1b6ee58

@ -1 +1 @@
Subproject commit 1ec27601f2e5dc20526824f3755a0b9cac217b02
Subproject commit 0a8af5fef5cc16d34732ddde7e684cfcc8ef53f7

@ -1 +1 @@
Subproject commit e305b9b6d1bbdcae5cfc470df66e14d22d3ef783
Subproject commit 416508587939217253959e54118eabe17eb9f0e9

@ -1 +1 @@
Subproject commit 26a4fa8040b8d2b0071a54c68c324dd4be45ef04
Subproject commit 2716284d7ca8ab2f48d2b18accadd36b352a7330

@ -1 +1 @@
Subproject commit d073a575b8c7c18131455b94ce2dfcb9b7aea4be
Subproject commit 95a28dae5e3ee55fd744d6f4a5fd5c9833151247

@ -1 +0,0 @@
Subproject commit 970c10a9399bf819f7f1bf209d1470ae8239c3c9

@ -1 +1 @@
Subproject commit 20370ad8e1b7e062ff44a1a9c088d9f84ebf3682
Subproject commit 616100d1693238228c89f7a38a6f7c022b8bf900

@ -1 +1 @@
Subproject commit ea2c1e0265b9fc0048969dfcbf72a0566c6b9ba6
Subproject commit 9b1ed9186bdb80299304de26568920c19df8ca06

@ -1 +1 @@
Subproject commit eccd14ab961a8d7fea200f38d447437a004f8423
Subproject commit 4d68a3736551020d4ff2bb7a7fdcd15028c0d080

@ -1 +1 @@
Subproject commit d64b402ac4c7d34574fb132ab360fddf785c626d
Subproject commit 7dab6c600dfe0af44cf8299a75825480864eed22

@ -1 +1 @@
Subproject commit 59a4a3550f08f1ca9a49793b6add94d169fd45c7
Subproject commit f5798ebfe22a4d1bfc7d7ffda9863776f12b8468

2
go.mod
View File

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

25
go.sum
View File

@ -11,8 +11,6 @@ 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/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/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/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/criyle/go-judge/pb v1.3.2 h1:S0c0EqRF+xePOwcZxSb9mPV+bkXgfOX9f7SQMrcdeb4=
@ -40,6 +38,8 @@ 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-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/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/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@ -102,16 +102,21 @@ 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=
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/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/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
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/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
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/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
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/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
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/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.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
@ -139,8 +144,10 @@ gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
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/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
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/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

View File

@ -3,12 +3,18 @@
// used for passing run time parameters.
package local
import "github.com/joint-online-judge/JOJ3/internal/stage"
import (
"os"
"github.com/joint-online-judge/JOJ3/internal/stage"
)
var name = "local"
type Local struct{}
func init() {
stage.RegisterExecutor(name, &Local{})
if os.Getenv("JOJ3_ENABLE_LOCAL_EXECUTOR") == "true" {
stage.RegisterExecutor(name, &Local{})
}
}

View File

@ -1,27 +1,16 @@
package sandbox
import (
"archive/tar"
"bytes"
"context"
"fmt"
"io"
"log/slog"
"maps"
"os"
"path/filepath"
"strings"
"github.com/criyle/go-judge/pb"
"github.com/joint-online-judge/JOJ3/internal/stage"
"google.golang.org/protobuf/proto"
)
const (
tarSizeThreshold = 128 * 1024 // 128 KB
tarCountThreshold = 100 // 100 files
)
func (e *Sandbox) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
var err error
if e.execClient == nil {
@ -31,51 +20,18 @@ func (e *Sandbox) Run(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
return nil, err
}
}
// cannot use range loop since we need to change the value
for i := 0; i < len(cmds); i += 1 {
if cmd := &cmds[i]; cmd.CopyIn == nil {
cmd := &cmds[i]
if cmd.CopyIn == nil {
cmd.CopyIn = make(map[string]stage.CmdFile)
}
for k, v := range cmds[i].CopyInCached {
for k, v := range cmd.CopyInCached {
if fileID, ok := e.cachedMap[v]; ok {
cmds[i].CopyIn[k] = stage.CmdFile{FileID: &fileID}
cmd.CopyIn[k] = stage.CmdFile{FileID: &fileID}
}
}
}
if needTar, tarData := prepareTar(cmds); needTar {
return e.runWithTar(cmds, tarData)
}
return e.runUnary(cmds)
}
func prepareTar(cmds []stage.Cmd) (bool, []byte) {
if len(cmds) == 0 {
return false, nil
}
for i := range cmds {
if shouldTar(&cmds[i]) {
tarData, keysInTar := createCopyInTar(&cmds[i])
if tarData == nil {
return false, nil
}
slog.Debug("prepareTar", "tarSize", len(tarData), "keysInTar", len(keysInTar))
for j := range cmds {
cmds[j].CopyInDir = ""
for _, k := range keysInTar {
delete(cmds[j].CopyIn, k)
}
}
return true, tarData
}
}
return false, nil
}
func shouldTar(cmd *stage.Cmd) bool {
size, count := estimateCopyIn(cmd)
return size >= tarSizeThreshold || count >= tarCountThreshold
}
func (e *Sandbox) runUnary(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
pbCmds := convertPBCmd(cmds)
for i, pbCmd := range pbCmds {
slog.Debug("sandbox execute", "i", i, "pbCmd size", proto.Size(pbCmd))
@ -97,213 +53,6 @@ func (e *Sandbox) runUnary(cmds []stage.Cmd) ([]stage.ExecutorResult, error) {
return results, nil
}
func (e *Sandbox) runWithTar(cmds []stage.Cmd, tarData []byte) ([]stage.ExecutorResult, error) {
fc := &pb.FileContent{}
fc.SetContent(tarData)
fileIDResp, err := e.execClient.FileAdd(context.TODO(), fc)
if err != nil {
return nil, fmt.Errorf("file add tar: %w", err)
}
fid := fileIDResp.GetFileID()
slog.Debug("sandbox tar uploaded", "fileID", fid, "tarSize", len(tarData))
defer func() {
deleteReq := &pb.FileID{}
deleteReq.SetFileID(fid)
if _, err := e.execClient.FileDelete(context.TODO(), deleteReq); err != nil {
slog.Warn("sandbox tar file delete", "fileID", fid, "error", err)
}
}()
tarFileName := "/w/__joj3_copyin.tar"
script := fmt.Sprintf(
"/bin/tar xf %s -C / --no-same-owner && rm -f %s && exec \"$@\"",
tarFileName, tarFileName,
)
for i := range cmds {
if cmds[i].CopyIn == nil {
cmds[i].CopyIn = make(map[string]stage.CmdFile)
}
cmds[i].CopyIn[tarFileName] = stage.CmdFile{FileID: &fid}
cmds[i].Args = append([]string{
"/bin/sh", "-c", script, "_",
}, cmds[i].Args...)
}
slog.Debug("sandbox tar exec", "cmd", cmds[0].Args[:3])
return e.runUnary(cmds)
}
func estimateCopyIn(cmd *stage.Cmd) (int, int) {
totalSize := 0
totalCount := 0
if cmd.CopyInDir != "" {
_ = filepath.Walk(cmd.CopyInDir,
func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() {
return nil
}
relPath, err := filepath.Rel(cmd.CopyInDir, path)
if err != nil {
return nil
}
if _, exists := cmd.CopyIn[relPath]; !exists {
totalSize += int(info.Size())
totalCount++
}
return nil
})
}
for _, f := range cmd.CopyIn {
if f.Symlink != nil {
continue
}
if f.Src != nil {
if fi, err := os.Stat(*f.Src); err == nil {
totalSize += int(fi.Size())
totalCount++
}
} else if f.Content != nil {
totalSize += len(*f.Content)
totalCount++
}
}
return totalSize, totalCount
}
func formatTarPath(p string) string {
if filepath.IsAbs(p) {
return strings.TrimPrefix(p, "/")
}
return "w/" + p
}
func createCopyInTar(cmd *stage.Cmd) ([]byte, []string) {
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
tarKeys := make([]string, 0, len(cmd.CopyIn))
if cmd.CopyInDir != "" {
err := filepath.Walk(cmd.CopyInDir,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(cmd.CopyInDir, path)
if err != nil {
return err
}
if relPath == "." {
return nil
}
if _, exists := cmd.CopyIn[relPath]; exists {
if info.IsDir() {
return filepath.SkipDir
}
return nil
}
if info.Mode()&os.ModeSymlink != 0 {
link, err := os.Readlink(path)
if err != nil {
return err
}
hdr, err := tar.FileInfoHeader(info, link)
if err != nil {
return err
}
hdr.Name = formatTarPath(relPath)
return tw.WriteHeader(hdr)
}
hdr, err := tar.FileInfoHeader(info, "")
if err != nil {
return err
}
hdr.Name = formatTarPath(relPath)
if info.IsDir() {
hdr.Name += "/"
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if info.IsDir() {
return nil
}
f, err := os.Open(path)
if err != nil {
return err
}
_, err = io.Copy(tw, f)
f.Close()
return err
})
if err != nil {
slog.Error("create copyIn tar walk", "error", err)
return nil, nil
}
}
for k, f := range cmd.CopyIn {
if f.FileID != nil || f.Symlink != nil || f.StreamIn || f.StreamOut || f.Pipe {
continue
}
if f.Content != nil {
hdr := &tar.Header{
Name: formatTarPath(k),
Mode: 0o644,
Size: int64(len(*f.Content)),
}
if err := tw.WriteHeader(hdr); err != nil {
slog.Error("create copyIn tar write header", "key", k, "error", err)
continue
}
if _, err := tw.Write([]byte(*f.Content)); err != nil {
slog.Error("create copyIn tar write content", "key", k, "error", err)
continue
}
tarKeys = append(tarKeys, k)
} else if f.Src != nil {
fi, err := os.Stat(*f.Src)
if err != nil {
slog.Error("create copyIn tar stat", "key", k, "src", *f.Src, "error", err)
continue
}
if fi.IsDir() {
continue
}
hdr, err := tar.FileInfoHeader(fi, "")
if err != nil {
slog.Error("create copyIn tar file info header", "key", k, "error", err)
continue
}
hdr.Name = formatTarPath(k)
if err := tw.WriteHeader(hdr); err != nil {
slog.Error("create copyIn tar write header", "key", k, "error", err)
continue
}
srcFile, err := os.Open(*f.Src)
if err != nil {
slog.Error("create copyIn tar open src", "key", k, "src", *f.Src, "error", err)
continue
}
_, err = io.Copy(tw, srcFile)
srcFile.Close()
if err != nil {
slog.Error("create copyIn tar copy", "key", k, "error", err)
continue
}
tarKeys = append(tarKeys, k)
}
}
if err := tw.Close(); err != nil {
slog.Error("create copyIn tar close", "error", err)
return nil, nil
}
return buf.Bytes(), tarKeys
}
func (e *Sandbox) Cleanup() error {
for k, fileID := range e.cachedMap {
req := &pb.FileID{}

View File

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

View File

@ -8,83 +8,13 @@ import (
"path/filepath"
"strings"
"unicode"
"unicode/utf8"
"github.com/go-git/go-git/v5"
"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.
// 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, whitelist map[rune]struct{}) ([]string, error) {
func getNonASCII(root string) ([]string, error) {
var nonASCII []string
gitattrExist := true
var matcher gitattributes.Matcher
@ -93,11 +23,6 @@ func getNonASCII(root string, whitelist map[rune]struct{}) ([]string, error) {
gitattrExist = false
}
submodules, err := getSubmodulePathsFromGoGit(root)
if err != nil {
return nil, err
}
if gitattrExist {
fs := os.DirFS(".")
f, err := fs.Open(".gitattributes")
@ -117,22 +42,18 @@ func getNonASCII(root string, whitelist map[rune]struct{}) ([]string, error) {
return err
}
relPath, err := filepath.Rel(root, path)
if err != nil {
return err
}
if info.IsDir() {
if info.Name() == ".git" {
return filepath.SkipDir
}
if _, isSubmodule := submodules[relPath]; isSubmodule {
return filepath.SkipDir
}
return nil
}
if gitattrExist {
relPath, err := filepath.Rel(root, path)
if err != nil {
return err
}
ret, matched := matcher.Match(strings.Split(relPath, "/"), nil)
if matched && ret["text"].IsUnset() && !ret["text"].IsSet() {
return nil
@ -149,9 +70,6 @@ func getNonASCII(root string, whitelist map[rune]struct{}) ([]string, error) {
for scanner.Scan() {
cont := true
for _, c := range scanner.Text() {
if _, ok := whitelist[c]; ok {
continue
}
if c > unicode.MaxASCII {
nonASCII = append(nonASCII, "\t"+path)
cont = false
@ -171,10 +89,8 @@ func getNonASCII(root string, whitelist map[rune]struct{}) ([]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.
// Additionally it accept a list of whitelisted characters that are allowed, repo-wide.
func NonASCIIFiles(root, whitelistedChars string) error {
whitelist := parseWhitelistedChars(whitelistedChars)
nonASCII, err := getNonASCII(root, whitelist)
func NonASCIIFiles(root string) error {
nonASCII, err := getNonASCII(root)
if err != nil {
slog.Error("getting non-ascii", "err", err)
return fmt.Errorf("error getting non-ascii: %w", err)