This commit is contained in:
2026-04-05 18:20:42 +03:00
commit 32737ee6d6
18 changed files with 2719 additions and 0 deletions

112
cmd/cli/main.go Normal file
View File

@@ -0,0 +1,112 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/Mond1c/judge/dsl"
"github.com/Mond1c/judge/reporter"
"github.com/Mond1c/judge/runner"
)
const usage = `judge — CI/CD testing system for student solutions
Usage:
judge <tests.jdg> <solution-dir> [flags]
Flags:
--json output as JSON instead of text
--wrapper <cmd> exec wrapper (e.g. "valgrind --error-exitcode=99")
--binary <name> name of executable produced by build (overrides .jdg)
--help show help
Example:
judge lab1.jdg ./student-solution
judge lab1.jdg ./student-solution --json
judge lab1.jdg ./student-solution --wrapper "valgrind --error-exitcode=99"
`
func main() {
args := os.Args[1:]
if len(args) == 0 || contains(args, "--help") || contains(args, "-h") {
fmt.Print(usage)
os.Exit(0)
}
if len(args) < 2 {
fmt.Fprintf(os.Stderr, "error: need <tests.jdg> and <solution-dir>\n\n%s", usage)
os.Exit(1)
}
testFile := args[0]
solutionDir := args[1]
jsonOutput := contains(args, "--json")
wrapper := flagValue(args, "--wrapper")
binary := flagValue(args, "--binary")
src, err := os.ReadFile(testFile)
if err != nil {
fatalf("cannot read %q: %v", testFile, err)
}
f, warns, err := dsl.Parse(string(src))
if err != nil {
fatalf("parse error in %q:\n %v", testFile, err)
}
for _, w := range warns {
fmt.Fprintf(os.Stderr, "warning: %s\n", w)
}
if _, err := os.Stat(solutionDir); err != nil {
fatalf("solution dir %q not found: %v", solutionDir, err)
}
r := runner.New(f, runner.Config{
WorkDir: solutionDir,
BinaryName: binary,
Wrapper: wrapper,
})
result := r.Run()
if jsonOutput {
if err := reporter.JSON(os.Stdout, result); err != nil {
fatalf("json output error: %v", err)
}
} else {
reporter.Text(os.Stdout, result)
}
if result.TotalScore < 0.9999 {
os.Exit(1)
}
}
func fatalf(msg string, args ...any) {
fmt.Fprintf(os.Stderr, "error: "+msg+"\n", args...)
os.Exit(1)
}
// flagValue returns the value of --name <value> or --name=value, else "".
func flagValue(args []string, name string) string {
prefix := name + "="
for i, a := range args {
if a == name && i+1 < len(args) {
return args[i+1]
}
if strings.HasPrefix(a, prefix) {
return a[len(prefix):]
}
}
return ""
}
func contains(slice []string, s string) bool {
for _, v := range slice {
if v == s {
return true
}
}
return false
}