Update doc: conf.toml detail

李衍志523370910113 2025-06-11 15:03:41 +08:00
parent f6c7b15d4a
commit b433ea5255

@ -1,69 +1,437 @@
# `conf.toml` Introduction
# JOJ3 configuration documentation for TAs
`conf.toml` file contains the details of steps that should be executed to the newly pushed code. This file will be placed in the repo of each student.
*Goals:* simple configuration files, which can easily be manually edited by TAs. These files are then parsed by `joj-config-generation` script to generate final internal `json` configurations files used by JOJ3.
Levels:
- repository: global repository configuration
- assignment: eg. homework or project
- task: eg. exercise or milestone
A task is composed of *stages* which are composed of one or more *steps*, eg. in stage "online-judge" each test-case can be viewed as a step
## Background
Brief introduction to the structure of JOJ configuration and how it impacts students' submissions.
### Configuration repository
All configuration is done through a `<course_name>-joj` repository. TAs connect to the repository, read the
`Readme.md`, and follow instructions to setup JOJ3 for a course. If previous TAs have already
prepared JOJ3, then only testing is required. Otherwise more setup is required.
The repository follow the server structure, so that deploying is can done easily using a simple
script. To find JOJ configuration files go to `home/tt/.config/joj`. Each folder should correspond
to a different assignment. This is where `.toml` files will have to be pushed in order to generate
the corresponding JOJ3 `conf.json` files.
### Commit messages
When using JOJ3, Commit messages must follow conventional commits format: `type(scope): message`, where `scope` is used to track the exact task which is being worked on.
Basic rules:
- health check is **always** run first
- all non JOJ stages are run on each push
- JOJ is only triggered if `joj` appears in `message`
Scope usage:
- `scope` must match JOJ configuration tree (`home/tt/.config/joj` subtree in `course-joj` repo), eg. if tree is `h1/ex2` the `scope` must be `h1/ex2`, if tree is `hw4/1` then scope is also `hw4/1`.
- if a scope is invalid then return a warning containing the list of valid scopes and exit nicely (either this is a typo, or this an exercise that do not require coding)
- as health check must always be run, `scope` should be checked after it
Refer to [Introduction to JOJ3](/Introduction-to-JOJ3.md) for more details on JOJ3 usage.
### TOML configuration format
All configurations files at human level must be written in TOML format. They will then be parsed to generate long and complete `.json` files that can be understantood by JOJ3.
Refer to [TOML reference guide](https://toml.io/en/v1.0.0). After writing a configuration file it is recommended to check its validity, eg. [TOML Lint](https://www.toml-lint.com/).
Converting the file into JSON format can help better visualize the structure which can be especially helpful when working with arrays of tables.
All JOJ3 configuration files can be found in `course-joj` repository under `home/tt/.config/joj`.
For each repository that will be used in the course (eg. `hw`, `p1`, and `p2`), create a
corresponding folder. This directory will be the `joj-root` for that repository, eg.
`home/tt/.config/joj/hw` is the `joj-root` for the `hw` repositories, ie. where JOJ3 configurations
for homework tasks will be setup.
## Repository level configuration
The first and most simple file to write is `repo.toml`. The template below can be used as a starter.
It contains the part of the configuration that will be used globally for all assignments and tasks.
It should be saved in the `joj-root` configuration for the repository.
- `grading_repo_name`: the name of the course joj repo, e.g. `ece280-joj`
- `max_total_score`: the total score of this assignment, typically set to `100`
- `max_size [float]`: maximum size allowed for a repo in MB
`[groups]`
- `name [List[str]]`: the restricted group
- `max_count [List[int]]`: the times for each words to be run within a period
- `time_period_hour [List[int]]`: the time period for `max_count`
- **Note:** the order of the above list matches for each keyword group.
`[files]`
- `required [array of string]`: files that are written by students and must be found in the repository
- `immutable [array of string]`: list all files managed by TT that students are forbidden to modify
- "Note:" carefully edit the `.gitignore` file that will be push to the students repo, there should be no **trailing spaces for each lines of the `.gitignore` file**.
**Important:**
- it's impossible to disable health check
- make the content in `.gitignore` **very strict** or students will push random files
- put this `repo.toml` file along with a `immutable_files` folder containing immutable files in the "sub directory" where you run the config generator containing all the configuration. An example can be found below:
<details><summary>Sample directory distribution.</summary>
```shell
.
├── hidden
│   ├── repo.toml
│   ├── immutable_files
│   │   ├── push.yaml
│   │   └── release.yaml
│   ├── ex1
│   │   ├── caseX.in
│   │   ├── caseX.out
│   │   ├── conf.json
│   │   └── conf.toml
│   └── p1
│   ├── caseX.in
│   ├── caseX.out
│   ├── conf.json
│   └── conf.toml
├── students
│   ├── repo.toml
│   ├── immutable_files
│   │   ├── push.yaml
│   │   └── release.yaml
│   ├── ex1
│   │   ├── caseX.in
│   │   ├── caseX.out
│   │   ├── conf.json
│   │   └── conf.toml
│   └── p1
│   ├── caseX.in
│   ├── caseX.out
│   ├── conf.json
│   └── conf.toml
├── students.csv
└── tools
├── .clang-tidy
└── compile
```
</details>
## Task level configuration
This configuration file will be used to generate the task level configuration of JOJ3. This file should therefore clearly describe what stages to run, how to run them, and what information to share back with students.
### General options
Global options:
- `task.name [string]`: name the task (eg. an exercise or project milestone)
- `release.stages [array of string]`: list all stages to run on a release (default: `[ ]`)
- `release.end_time [offset date-time]`: RFC 3339 formatted date-time with offset
- `release.begin_time [offset date-time]`: RFC 3339 formatted date-time with offset
- `limit.cpu [int]`: default maximum running time used for all stages in sec (default: `4`)
- `limit.mem [int]`: default maximum amount of RAM allowed for all stages in MB (default: `4`)
- `limit.stdout [int]`: default `stdout` size applicable to all stages in kB (default: `4`)
- `limit.stderr [int]`: default `stderr` size applicable to all stages in kB (default: `4`)
Each stage is configured in a table. All parameters following a table definition belong to it until
the next table is defined.
`[stagename]` table: configuration for stage `stagename`
- `command [string]`: command to run
- `files.import [array of string]`: list of files to import to ensure the command runs as expected (eg.
driver and header files needed for compilation), path starts from JOJ configuration directory (eg. to import `/home/tt/.config/joj/tools/matlab-joj` use path `"tools/matlab-joj"`)
- `files.export [array of string]`: list of generated files to export to ensure future commands run as expected (eg.
binaries needed for online-judge stages)
- `name [string]`: stage name to display (default: `stagename`)
- `parsers [array of string]`: list of parsers to run on the output of `command` (default: `[ "result-status" ]`)
- `limit.cpu [int]`: maximum running time for the stage in sec (default: `4`)
- `limit.mem [int]`: maximum amount of RAM allowed for the stage in MB (default: `4`)
- `limit.stdout [int]`: default `stdout` size applicable to the stage in kB (default: `4`)
- `limit.stderr [int]`: default `stderr` size applicable to the stage in kB (default: `4`)
### Online judge options
While online judge stages work in a similar way as other stages, they often feature more than one step. For instance, while a compilation or code quality stage
is composed of a single step where the code is compiled or analysed, an online judge stage can features many steps, each corresponding to a test-case.
Therefore, on-top of the regular stage options, online-judge stages can be adjusted at "step level ", ie. for each test-case.
Any online-judge stage *must* feature the keyword `judge`, eg. `judge`, `judge-base`, `asan-judge` are all valid online-judge
stage names while `oj`, `run-base`, and `asan` are not.
The following extra option is available for online-judge stages:
- `skip [array of string]`: list of test cases to skip for this stage (default: `[ ]`)
For each step, ie. test-case, the following configuration can be adjusted:
- `limit.cpu [int]`: maximum running time for the stage in sec (default: `4`)
- `limit.mem [int]`: maximum amount of RAM allowed for the stage in MB (default: `4`)
### Parsers
Currently the following parsers are available:
- Generic:
- `dummy`: output the score and comment
- `keyword`: catch keywords on any generic text output and can force quit on a match
- `result-detail`: provide basic statistics on memory and CPU usage
- `result-status`: check if the executor exit with status accepted, if not quit with error status
- Code quality:
- `clangtidy`: parse clang-tidy output for specified keywords
- `cppcheck`: parse cppcheck output for specified keywords
- `cpplint`: parse cpplint output for specified keywords
- `elf`: parse elf output for specified keywords
- Online judge
- `diff`: difference between the output and a provided file content (commonly used for judge stage)
Parsers can be combined. For instance one might want to show the `diff`, `result-status`, and `result-detail`
outputs for the online judge. Some parsers can also be further configured.
#### Dummy parser options
- `comment [string]`: display any text
- `forcequit [boolean]`: quit at the end of the stage if a test case fails (default: `false`)
- `score [int]`: score for passing dummy stage (default: `0`)
*Notes.*
- Adding a dummy parser can be useful to add extra comments or separate parsers output.
- The `forcequit` option can be used to prevent students from using a specific scope in their commit message.
#### Keyword parser
- `forcequit [boolean]`: quit at the end of the stage if there is a match
- `keyword [array of string]`: list of keywords to catch on `stdout`
- `weight [array of int]`: corresponding weight for each keyword caught on `stdout`
*Note.* This parser can be used to catch words on the output of any code quality tool which
doesn't already have a parser implement in JOJ3
#### Result-detail parser
- `exitstatus [boolean]`: display exit status (default: `false`)
- `mem [boolean]`: display the memory usage (default: `true`)
- `stderr [boolean]`: display `stderr` messages (default: `false`)
- `stdout [boolean]`: display `stdout` messages (default: `false`)
- `time [boolean]`: display run time (default: `true`)
#### Result-status parser
- `comment [string]`: comment to display on successful run
- `forcequit [boolean]`: quit at the end of the stage if if the error status is not 0 (default: `true`)
- `score [int]`: score to assign on a successful run (default: `0`)
*Note.* The main benefit of this parser is that it quits on failure. It could be used to exit at
the end of a failed compilation stage.
#### Clangtidy, cppcheck, and cpplint parsers
- `keyword [array of string]`: list of keywords to catch on `stdout`
- `weight [array of int]`: corresponding weight for each keyword caught on `stdout`
- `forcequit [boolean]`: quit at the end of the stage if if the score is different from the initial on, ie. if issues were found (default: `false`)
#### Elf parser
Not ready yet.
#### Diff parser options
- `comment.pass [string]`: comment to display when passing a test case (default: `"🥳Passed!"`)
- `comment.fail [string]`: comment to display when failing a test case (default: `"🧐Failed..."`)
- `forcequit [boolean]`: quit at the end of the stage if a test case fails (default: `false`)
- `output.hide [boolean]`: hide diff output (default: `false`)
- `output.ignore_spaces [boolean]`: ignore white spaces in diff output (default: `true`)
- `score [int]`: score awarded for passing the test case (default: `0`)
*Note.* This parser can be configured and adjusted for each test-case (others parsers are configured at stage level)
## Examples
<details><summary>Sample repository configuration.</summary>
The `conf.toml` file will begin with:
```toml
logLevel = -4
sandboxExecServer = "172.17.0.1:5051"
grading_repo_name = "ece280-joj"
sandbox_token = "test"
max_total_score = 100
max_size = 50.5
# for tests
[groups]
name = ["joj", "run"]
max_count = [1000, 1000]
time_period_hour = [24, 24]
[files]
required = ["README.md", "Changelog.md"]
immutable = [".gitignore", ".gitattributes",".gitea/workflows/push.yaml", ".gitea/workflows/release.yaml"]
```
which helps logging into the server.
</details>
In the file, there will be many stages stating each parser to run. To be more specific, the stages:
```markdown
- healthcheck
- compile
- run
```
and the parsers:
```markdown
- clangtidy
- cppcheck
- cpplint
- diff
- dummy
- healthcheck
- keyword
- resultstatus
```
During the `run-joj` process, we will have **several stages** where we will apply different parsers to the code. Those **stages and parsers** are what the users should know how to set. The sandbox executor always sends to go-judge. Stages run one by one. Each stage contains an executor that runs command and give the command output, and a parser that parse the output and generate score and comment.
## Template
<details><summary>Sample basic task configuration for MATLAB where most default options are used.</summary>
Here are the template code for stages and parsers:
```toml
task.name="hw3 ex5"
release.end_time= 2024-10-18 23:59:00+08:00
release.begin_time= 2024-10-17 23:59:00+08:00
[[stages]]
name = "" # stage name here
[stages.executor]
name = "sandbox"
[stages.executor.with.default]
args = [] # giving args here
env = ["PATH=/usr/bin:/bin:/usr/local/bin"] # provide env, usually set it as this example
name = "judge-base"
command="./matlab-joj ./h3/ex5.m"
files.import = [ "tools/matlab-joj", "tools/matlab_formatter.py" ]
score = 100
# cpu, memory and proc limit
cpuLimit = 10_000_000_000
memoryLimit = 104_857_600
procLimit = 50
parsers = ["diff", "result-detail"]
result-detail.time = false
result-detail.mem = false
result-detail.stderr = true
```
copyInCwd = true
copyOut = ["stdout", "stderr"]
[stages.executor.with.default.copyIn.healthcheck]
src = "/usr/local/bin/healthcheck"
copyOut = ["stdout", "stderr"]
[stages.executor.with.default.stdin]
content = ""
[stages.executor.with.default.stdout]
name = "stdout"
max = 4_096
[stages.executor.with.default.stderr]
name = "stderr"
max = 4_096
</details>
# configure parser
[stages.parser]
name = "" # set parser name
[stages.parser.with]
score = 0
comment = "" # comment from the conf file
```
<details><summary>Sample advanced task configuration for C where many defaults are overwritten.</summary>
```toml
# general task configuration
task="Homework 1 exercise 2" # task name
release.deadline = 2024-10-12 23:59:00+08:00
release.stages = [ "compile" ]
[[stages]]
name = "Compilation"
command = "make.sh" # eg. script running cmake commands
files.import = [ "tools/make.sh", "src/main.c", "src/task.h", "srcCMakelist.txt" ]
files.export = [ "driver", "p2", "p2-msan" ]
limit.cpu = 180 # p2 takes long to compile
limit.stderr = 128
# compile parsers
parsers = [ "result-detail", "dummy", "result-status" ]
result-status.comment = "Congratulations! Your code compiled successfully."
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stderr = true
result-detail.time = false
result-detail.mem = false
[[stages]]
name = "File length check"
command = "./file-length 500 400 *.c *.h" # command to run
files.import = [ "tools/file-length" ]
parsers = [ "keyword", "dummy", "result-detail" ]
keyword.keyword = [ "max", "recommend"] # keywords caught by corresponding JOJ plugin
keyword.weight = [ 50, 20 ] # weight of each keyword
result-detail.exitstatus = true
result-detail.stderr = true
result-detail.time = false
result-detail.mem = false
[[stages]]
name = "Clang-tidy checks"
command = "run-clang-tidy-18 -header-filter=.* -quiet -load=/usr/local/lib/libcodequality.so -p build"
limit.stdout = 65
parsers = [ "clangtidy", "dummy", "result-detail" ]
clangtidy.keyword = [ "codequality-no-global-variables", "codequality-no-header-guard", "readability-function-size", "readability-duplicate-include", "readability-identifier-naming", "readability-redundant", "readability-misleading-indentation", "readability-misplaced-array-index", "cppcoreguidelines-init-variables", "bugprone-suspicious-string-compare", "google-global-names-in-headers", "clang-diagnostic", "clang-analyzer", "misc performance" ]
clangtidy.weight = [10, 10, 50, 10, 5, 5, 10, 5, 5, 8, 5, 5, 5, 5]
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stdout = true
result-detail.time = false
result-detail.mem = false
[[stages]]
name = "Cppcheck check"
command = "cppcheck --template='{\"file\":\"{file}\",\"line\":{line}, \"column\":{column}, \"severity\":\"{severity}\", \"message\":\"{message}\", \"id\":\"{id}\"}' --force --enable=all --quiet ./"
limit.stderr = 65
parsers = [ "cppcheck", "dummy", "result-detail" ]
cppcheck.keyword = ["error", "warning", "portability", "performance", "style"]
cppcheck.weight = [20, 10, 15, 15, 10]
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stderr = true
result-detail.time = false
result-detail.mem = false
[[stages]]
name = "Cpplint check"
command = "cpplint --linelength=120 --filter=-legal,-readability/casting,-whitespace,-runtime/printf,-runtime/threadsafe_fn,-readability/todo,-build/include_subdir,-build/header_guard --recursive --exclude=build ."
limit.stdout = 65
parsers = [ "cpplint", "dummy", "result-detail" ]
cpplint.keyword = [ "runtime", "readability", "build" ]
cpplint.weight = [ 10, 20, 15]
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stdout = true
result-detail.time = false
result-detail.mem = false
[[stages]]
name = "judge-base"
command="./driver ./mumsh"
limit.cpu = 3
limit.mem = 75
score = 10
parsers = ["diff", "dummy", "result-detail"]
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stderr = true
case4.score = 15
case4.limit.cpu = 30
case4.limit.mem = 10
case4.limit.stdout = 8
case5.score = 25
case8.limit.stderr = 128
[[stages]]
name = "judge-msan"
command="./driver ./mumsh-msan"
limit.cpu = 10 # default cpu limit (in sec) for each test case
limit.mem = 500 # set default mem limit (in MB) for all OJ test cases
score = 10
skip = ["case0", "case11"]
parsers = ["diff", "dummy", "result-detail"]
dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stderr = true
case4.score = 15
case4.limit.cpu = 30
case4.limit.mem = 10
case5.diff.output.ignore_spaces = false
case6.diff.output.hide = true
```
</details>
## Distribute
After you properly write your toml files and properly structure your directories, use `joj3-config-generator` to convert those toml file into json files. The procedure to deploy and use it can be find [here](https://github.com/joint-online-judge/JOJ3-config-generator).