Course Grader
个人项目 · 全栈与平台工程Personal project · full-stack & platform engineering

大学编程课的自动判分平台,跑在自托管 GitLab 上Autograding for university programming courses, on self-managed GitLab

学生 git push,可信流水线在离线沙箱里给这次提交判分,成绩以只新增的记录存进 Git,老师和学生在门户里看结果。Students git push; a trusted pipeline grades that exact commit in an offline sandbox, the result is stored as an append-only record in Git, and teachers and students see it in a web portal.

演示用合成数据,选老师或学生身份即可浏览每个页面;改动不会保存。隐藏测试和参考答案不在演示里。Synthetic data; pick the teacher or a student and click through every page. Changes are not saved. Hidden tests and reference solutions are not part of the demo.

Student workspace: task on the left, editor on the right, Run and Submit at the bottom

测出来的数字Measured, not guessed

单台主机、合成学生;每个数字都注明测的是哪一段。One host, synthetic students; each number says what it measures.

40.7 sp95 从 push 到出分p95 push → graded10 分钟 300 次 push,经过完整 GitLab 链路,0 误判(目标 120 秒)300 pushes in 10 minutes through the whole GitLab chain, 0 mis-graded (target 120 s)
54 / 54真实 GitLab 验收real-GitLab acceptance checksGitLab CE 19.4.1 与极狐 GitLab 19.4.2 上全部通过pass on GitLab CE 19.4.1 and JiHu GitLab 19.4.2
66.7 → 23.4 s单份判分中位延迟median grading-job latency100 人班级,每份提交一个沙箱容器;两轮分数逐人相同100-student class, one sandbox per submission; identical scores in both runs
61 → 0截止高峰误判(120 份)mis-graded in a deadline burst (of 120)不限并发时 CPU 争抢让正确答案超时;加 Go 执行器统一排队后为 0CPU contention timed out correct answers; 0 through the Go runner's host-wide queue

怎么工作How it works

GitLab 是唯一的数据来源,门户只是读出来的视图。GitLab is the source of truth; the portal is a view of it.

学生 pushStudent pushes

私有仓库,按服务器接收时间判断是否按时to a private repo; on-time is judged by the server's receive time

可信流水线Trusted pipeline

webhook 触发 cg dispatch,学生改不了判分配置a webhook starts cg dispatch; students cannot change grading

离线沙箱Offline sandbox

无网络、只读、非 root、去掉全部 capability、资源限额no network, read-only, non-root, no capabilities, resource limits

成绩记录Grade record

只新增的 JSON 提交,记下提交、判分器版本、镜像摘要append-only JSON commit naming the commit, grader version and image digest

门户Portal

学生看公开用例详情和隐藏类别总分;老师看全班students see public-test detail and hidden-category totals; staff see the class

界面Screens

截图来自上面的演示(合成数据)。Taken from the demo above (synthetic data).

做了什么What is in it

每份提交一个沙箱One sandbox per submission

用静态 C 程序作容器 PID 1 跑每个用例,用例之间核验清理;任何一步无法确认就退回逐用例容器,只会更快、不改分数。A static C program runs each case as PID 1 and verifies clean-up between cases; anything unconfirmed falls back to a container per case, so it is only ever faster, never a different score.

发布前检查测试Tests checked before release

TestGuard 用参考解答做差分,找出太松或太严的测试;在一次真实小测里发现测试过严,修正后重判。TestGuard diffs against the reference solution to find tests that are too loose or too strict; it caught a too-strict real quiz, fixed and regraded.

像 Ed 一样作答An Ed-style workspace

网页里读题、写代码、运行、终端、提交,结果当场显示;自测、笔记、求助,学生的私人学习数据老师看不到。Read, edit, run, terminal and submit in the browser, with results in place; self-tests, notes and help requests, private to the student.

可追溯的成绩Traceable grades

判分器只新建记录;重判是新文件;老师的认定、调整、发布都写审计。The grader only creates records; a regrade is a new file; finalizing, adjusting and publishing are audited.

可部署Deployable

一条命令在一台 Ubuntu 服务器上装好 GitLab、门户、HTTPS、Runner 和每日备份。One command sets up GitLab, the portal, HTTPS, runners and nightly backups on an Ubuntu server.

测试Tested

983 个 Python 测试、113 个前端单测、92 个页面的浏览器截断检查。983 Python tests, 113 frontend unit tests and a 92-page browser clipping check.

技术Stack

PythonFastAPIReactTypeScriptGoDockerGitLab CIPostgreSQLRedisPrometheusCaddy