WIP: dev #4

Closed
张泊明518370910136 wants to merge 48 commits from dev into master
17 changed files with 3333 additions and 162 deletions

2
.gitignore vendored
View File

@ -15,7 +15,7 @@ dist/
downloads/ downloads/
eggs/ eggs/
.eggs/ .eggs/
lib/ # lib/
lib64/ lib64/
parts/ parts/
sdist/ sdist/

View File

@ -1,14 +1,23 @@
from typing import List from typing import List
from joj3_config_generator.lib.repo import getHealthcheckConfig
from joj3_config_generator.lib.task import (
fix_diff,
fix_dummy,
fix_keyword,
fix_result_detail,
get_conf_stage,
get_executorWithConfig,
)
from joj3_config_generator.models import joj1, repo, result, task from joj3_config_generator.models import joj1, repo, result, task
# FIXME: LLM generated convert function, only for demostration
def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config: def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
# Create the base ResultConf object # Create the base ResultConf object
result_conf = result.Config( result_conf = result.Config(
name=task_conf.task, name=task_conf.task,
log_path=f"{task_conf.task.replace(' ', '_')}.log", # TODO: specify the exact folder difference
log_path=f"{task_conf.task.replace(' ', '-')}.log",
expire_unix_timestamp=( expire_unix_timestamp=(
int(task_conf.release.deadline.timestamp()) int(task_conf.release.deadline.timestamp())
if task_conf.release.deadline if task_conf.release.deadline
@ -18,36 +27,18 @@ def convert(repo_conf: repo.Config, task_conf: task.Config) -> result.Config:
teapot=result.Teapot(), teapot=result.Teapot(),
) )
# Construct healthcheck stage
healthcheck_stage = getHealthcheckConfig(repo_conf, task_conf)
result_conf.stage.stages.append(healthcheck_stage)
cached: list[str] = []
# Convert each stage in the task configuration # Convert each stage in the task configuration
for task_stage in task_conf.stages: for task_stage in task_conf.stages:
executor_with_config = result.ExecutorWith( executor_with_config, cached = get_executorWithConfig(task_stage, cached)
default=result.Cmd( conf_stage = get_conf_stage(task_stage, executor_with_config)
args=task_stage.command.split(), conf_stage = fix_result_detail(task_stage, conf_stage)
copy_in={ conf_stage = fix_dummy(task_stage, conf_stage)
file: result.CmdFile(src=file) for file in task_stage.files.import_ conf_stage = fix_keyword(task_stage, conf_stage)
}, conf_stage = fix_diff(task_stage, conf_stage)
copy_out_cached=task_stage.files.export,
),
cases=[], # You can add cases if needed
)
conf_stage = result.StageDetail(
name=task_stage.name,
group=task_conf.task,
executor=result.Executor(
name="sandbox",
with_=executor_with_config,
),
parsers=[
result.Parser(name=parser, with_={}) for parser in task_stage.parsers
],
)
if "result-detail" in task_stage.parsers:
result_detail_parser = next(
p for p in conf_stage.parsers if p.name == "result-detail"
)
result_detail_parser.with_.update(task_stage.result_detail)
result_conf.stage.stages.append(conf_stage) result_conf.stage.stages.append(conf_stage)
return result_conf return result_conf
@ -77,7 +68,6 @@ def convert_joj1(joj1_conf: joj1.Config) -> task.Config:
files=files, files=files,
score=score, score=score,
parsers=parsers, parsers=parsers,
result_detail=task.ParserResultDetail(), # You can customize this further if needed
) )
) )
# Assuming no deadline is provided in `joj1`, you can set it accordingly # Assuming no deadline is provided in `joj1`, you can set it accordingly

View File

View File

@ -0,0 +1,99 @@
import hashlib
import socket
from joj3_config_generator.models import joj1, repo, result, task
def getGradingRepoName() -> str:
host_name = socket.gethostname()
return f"{host_name.split('-')[0]}-joj"
def getTeapotConfig(repo_conf: repo.Config, task_conf: task.Config) -> result.Teapot:
teapot = result.Teapot(
# TODO: fix the log path
log_path=f"{task_conf.task.replace(' ', '-')}-joint-teapot-debug.log",
scoreboard_path=f"{task_conf.task.replace(' ', '-')}-scoreboard.csv",
failed_table_path=f"{task_conf.task.replace(' ', '-')}-failed-table.md",
grading_repo_name=getGradingRepoName(),
)
return teapot
def getHealthcheckCmd(repo_conf: repo.Config) -> result.Cmd:
repoSize = repo_conf.max_size
immutable = repo_conf.files.immutable
repo_size = f"-repoSize={str(repoSize)} "
required_files = repo_conf.files.required
for i, meta in enumerate(required_files):
required_files[i] = f"-meta={meta} "
immutable_files = f"-checkFileNameList="
for i, name in enumerate(immutable):
if i == len(immutable) - 1:
immutable_files = immutable_files + name + " "
else:
immutable_files = immutable_files + name + ","
# FIXME: need to make solution and make things easier to edit with global scope
chore = f"/tmp/repo-health-checker -root=. "
args = ""
args = args + chore
args = args + repo_size
for meta in required_files:
args = args + meta
args = args + get_hash(immutable)
args = args + immutable_files
cmd = result.Cmd(
args=args.split(),
# FIXME: easier to edit within global scope
copy_in={
f"/tmp/repo-health-checker": result.CmdFile(src=f"/tmp/repo-health-checker")
},
)
return cmd
def getHealthcheckConfig(
repo_conf: repo.Config, task_conf: task.Config
) -> result.StageDetail:
healthcheck_stage = result.StageDetail(
name="healthcheck",
group="",
executor=result.Executor(
name="sandbox",
with_=result.ExecutorWith(default=getHealthcheckCmd(repo_conf), cases=[]),
),
parsers=[result.Parser(name="healthcheck", with_={"score": 0, "comment": ""})],
)
return healthcheck_stage
def calc_sha256sum(file_path: str) -> str:
sha256_hash = hashlib.sha256()
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(65536 * 2), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
def get_hash(immutable_files: list[str]) -> str: # input should be a list
file_path = "../immutable_file/" # TODO: change this when things are on the server
immutable_hash = []
for i, file in enumerate(immutable_files):
immutable_files[i] = file_path + file.rsplit("/", 1)[-1]
for i, file in enumerate(immutable_files):
immutable_hash.append(calc_sha256sum(file))
hash_check = "-checkFileSumList="
for i, file in enumerate(immutable_hash):
if i == len(immutable_hash) - 1:
hash_check = hash_check + file + " "
else:
hash_check = hash_check + file + ","
return hash_check

View File

@ -0,0 +1,268 @@
from typing import Tuple
import rtoml
from joj3_config_generator.models import joj1, repo, result, task
def get_conf_stage(
task_stage: task.Stage, executor_with_config: result.ExecutorWith
) -> result.StageDetail:
conf_stage = result.StageDetail(
name=task_stage.name if task_stage.name is not None else "",
# TODO: we may have cq in future
group=(
"joj"
if (task_stage.name is not None) and ("judge" in task_stage.name)
else None
),
executor=result.Executor(
name="sandbox",
with_=executor_with_config,
),
parsers=(
[result.Parser(name=parser, with_={}) for parser in task_stage.parsers]
if task_stage.parsers is not None
else []
),
)
return conf_stage
def get_executorWithConfig(
task_stage: task.Stage, cached: list[str]
) -> Tuple[result.ExecutorWith, list[str]]:
file_import = (
task_stage.files.import_
if hasattr(task_stage, "files")
and hasattr(task_stage.files, "import_")
and (task_stage.files is not None)
and (task_stage.files.import_ is not None)
else []
)
copy_in_files = [file for file in file_import if (file not in cached)]
file_export = (
task_stage.files.export
if hasattr(task_stage, "files")
and hasattr(task_stage.files, "export")
and (task_stage.files is not None)
else []
)
executor_with_config = result.ExecutorWith(
default=result.Cmd(
args=(task_stage.command.split() if task_stage.command is not None else []),
copy_in={
file: result.CmdFile(src=f"/home/tt/.config/joj/{file}")
for file in copy_in_files
},
copy_in_cached={file: file for file in copy_in_files},
copy_out_cached=file_export if file_export is not None else [],
cpu_limit=(
task_stage.limit.cpu * 1_000_000_000
if task_stage.limit is not None and task_stage.limit.cpu is not None
else 4 * 1_000_000_000
),
clock_limit=(
2 * task_stage.limit.cpu * 1_000_000_000
if task_stage.limit is not None and task_stage.limit.cpu is not None
else 8 * 1_000_000_000
),
memory_limit=(
task_stage.limit.mem * 1_024 * 1_024
if task_stage.limit is not None and task_stage.limit.mem is not None
else 4 * 1_024 * 1_024
),
stderr=result.CmdFile(
name="stderr",
max=(
task_stage.limit.stderr * 1_000_000_000
if task_stage.limit is not None
and task_stage.limit.stderr is not None
else 4 * 1_024 * 1_024
),
),
stdout=result.CmdFile(
name="stdout",
max=(
task_stage.limit.stdout * 1_000_000_000
if task_stage.limit is not None
and task_stage.limit.stdout is not None
else 4 * 1_024 * 1_024
),
),
),
cases=[], # You can add cases if needed
)
if file_export is not None:
for file in file_export:
if file not in cached:
cached.append(file)
return (executor_with_config, cached)
def fix_keyword(
task_stage: task.Stage, conf_stage: result.StageDetail
) -> result.StageDetail:
keyword_parser = ["clangtidy", "keyword", "cppcheck", "cpplint"]
if task_stage.parsers is not None:
for parser in task_stage.parsers:
if parser in keyword_parser:
keyword_parser_ = next(
p for p in conf_stage.parsers if p.name == parser
)
keyword_weight = []
if getattr(task_stage, parser, None) is not None:
unique_weight = list(set(getattr(task_stage, parser).weight))
for score in unique_weight:
keyword_weight.append({"keywords": [], "score": score})
for idx, score in enumerate(unique_weight):
for idx_, score_ in enumerate(
getattr(task_stage, parser).weight
):
if score == score_:
keyword_weight[idx]["keywords"].append(
getattr(task_stage, parser).keyword[idx_]
)
else:
continue
keyword_parser_.with_.update({"matches": keyword_weight})
else:
continue
return conf_stage
def fix_result_detail(
task_stage: task.Stage, conf_stage: result.StageDetail
) -> result.StageDetail:
if (task_stage.parsers is not None) and ("result-detail" in task_stage.parsers):
result_detail_parser = next(
p for p in conf_stage.parsers if p.name == "result-detail"
)
if task_stage.result_detail is not None:
show_files = []
if (
task_stage.result_detail.stdout
and task_stage.result_detail.stdout is not None
):
show_files.append("stdout")
if (
task_stage.result_detail.stderr
and task_stage.result_detail.stderr is not None
):
show_files.append("stderr")
result_detail_parser.with_.update(
{
"score": 0,
"comment": "",
"showFiles": show_files,
"showExitStatus": task_stage.result_detail.exitstatus,
"showRuntime": task_stage.result_detail.time,
"showMemory": task_stage.result_detail.mem,
}
)
return conf_stage
def fix_dummy(
task_stage: task.Stage, conf_stage: result.StageDetail
) -> result.StageDetail:
dummy_parser = [
"dummy",
"result-status",
"cpplint",
]
if task_stage.parsers is not None:
for parser in task_stage.parsers:
if parser in dummy_parser:
dummy_parser_ = next(p for p in conf_stage.parsers if p.name == parser)
if (
getattr(task_stage, parser.replace("-", "_"), None) is not None
) and (task_stage.result_status is not None):
dummy_parser_.with_.update(
{
"score": task_stage.result_status.score,
"comment": task_stage.result_status.comment,
"forceQuitOnNotAccepted": task_stage.result_status.forcequit,
}
)
else:
continue
return conf_stage
def fix_diff(
task_stage: task.Stage, conf_stage: result.StageDetail
) -> result.StageDetail:
if task_stage.parsers is not None and "diff" in task_stage.parsers:
diff_parser = next((p for p in conf_stage.parsers if p.name == "diff"), None)
skip = task_stage.skip or []
cases = task_stage.cases or {}
finalized_cases = [case for case in cases if case not in skip]
stage_cases = []
parser_cases = []
for case in finalized_cases:
case_stage = task_stage.cases.get(case) if task_stage.cases else None
if not case_stage:
continue
# Ensure case_stage.limit is defined before accessing .cpu and .mem
cpu_limit = (
case_stage.limit.cpu * 1_000_000_000
if case_stage.limit and case_stage.limit.cpu is not None
else 0
)
clock_limit = (
2 * case_stage.limit.cpu * 1_000_000_000
if case_stage.limit and case_stage.limit.cpu is not None
else 0
)
memory_limit = (
case_stage.limit.mem * 1_024 * 1_024
if case_stage.limit and case_stage.limit.mem is not None
else 0
)
stage_cases.append(
result.OptionalCmd(
stdin=result.CmdFile(
src=f"/home/tt/.config/joj/{conf_stage.name}/{case}.in"
),
cpu_limit=cpu_limit,
clock_limit=clock_limit,
memory_limit=memory_limit,
proc_limit=50,
)
)
# Ensure case_stage.diff and case_stage.diff.output are defined
diff_output = (
case_stage.diff.output
if case_stage.diff and case_stage.diff.output
else None
)
if diff_output:
parser_cases.append(
{
"outputs": [
{
"score": diff_output.score,
"fileName": "stdout",
"answerPath": f"/home/tt/.config/joj/{conf_stage.name}/{case}.out",
"forceQuitOnDiff": diff_output.forcequit,
"alwaysHide": diff_output.hide,
"compareSpace": not diff_output.ignorespaces,
}
]
}
)
if diff_parser and task_stage.diff is not None:
diff_parser.with_.update({"name": "diff", "cases": parser_cases})
conf_stage.executor.with_.cases = stage_cases
return conf_stage

View File

@ -9,7 +9,7 @@ import yaml
from joj3_config_generator.convert import convert as convert_conf from joj3_config_generator.convert import convert as convert_conf
from joj3_config_generator.convert import convert_joj1 as convert_joj1_conf from joj3_config_generator.convert import convert_joj1 as convert_joj1_conf
from joj3_config_generator.models import joj1, repo, task from joj3_config_generator.models import joj1, repo, result, task
from joj3_config_generator.utils.logger import logger from joj3_config_generator.utils.logger import logger
app = typer.Typer(add_completion=False) app = typer.Typer(add_completion=False)
@ -46,15 +46,15 @@ def convert_joj1(yaml_file: typer.FileText, toml_file: typer.FileTextWrite) -> N
@app.command() @app.command()
def convert(root: Path = Path(".")) -> None: def convert(root: Path = Path(".")) -> result.Config:
""" """
Convert given dir of JOJ3 toml config files to JOJ3 json config files Convert given dir of JOJ3 toml config files to JOJ3 json config files
""" """
logger.info(f"Converting files in {root.absolute()}") logger.info(f"Converting files in {root.absolute()}")
repo_toml_path = os.path.join(root, "repo.toml") repo_toml_path = os.path.join(root.absolute(), "basic", "repo.toml")
# TODO: loop through all dirs to find all task.toml # TODO: loop through all dirs to find all task.toml
task_toml_path = os.path.join(root, "task.toml") task_toml_path = os.path.join(root.absolute(), "basic", "task.toml")
result_json_path = os.path.join(root, "task.json") result_json_path = os.path.join(root.absolute(), "basic", "task.json")
with open(repo_toml_path) as repo_file: with open(repo_toml_path) as repo_file:
repo_toml = repo_file.read() repo_toml = repo_file.read()
with open(task_toml_path) as task_file: with open(task_toml_path) as task_file:
@ -63,6 +63,9 @@ def convert(root: Path = Path(".")) -> None:
task_obj = rtoml.loads(task_toml) task_obj = rtoml.loads(task_toml)
result_model = convert_conf(repo.Config(**repo_obj), task.Config(**task_obj)) result_model = convert_conf(repo.Config(**repo_obj), task.Config(**task_obj))
result_dict = result_model.model_dump(by_alias=True) result_dict = result_model.model_dump(by_alias=True)
with open(result_json_path, "w") as result_file: with open(result_json_path, "w") as result_file:
json.dump(result_dict, result_file, ensure_ascii=False, indent=4) json.dump(result_dict, result_file, ensure_ascii=False, indent=4)
result_file.write("\n") result_file.write("\n")
return result_model

View File

@ -9,7 +9,7 @@ class CmdFile(BaseModel):
content: Optional[str] = None content: Optional[str] = None
file_id: Optional[str] = Field(None, serialization_alias="fileId") file_id: Optional[str] = Field(None, serialization_alias="fileId")
name: Optional[str] = None name: Optional[str] = None
max: Optional[int] = None max: Optional[int] = 4 * 1024 * 1024
symlink: Optional[str] = None symlink: Optional[str] = None
stream_in: bool = Field(False, serialization_alias="streamIn") stream_in: bool = Field(False, serialization_alias="streamIn")
stream_out: bool = Field(False, serialization_alias="streamOut") stream_out: bool = Field(False, serialization_alias="streamOut")
@ -17,17 +17,17 @@ class CmdFile(BaseModel):
class Cmd(BaseModel): class Cmd(BaseModel):
args: List[str] args: list[str]
env: List[str] = [] env: list[str] = ["PATH=/usr/bin:/bin:/usr/local/bin"]
stdin: Optional[CmdFile] = None stdin: Optional[CmdFile] = CmdFile(content="")
stdout: Optional[CmdFile] = None stdout: Optional[CmdFile] = CmdFile(name="stdout", max=4 * 1024)
stderr: Optional[CmdFile] = None stderr: Optional[CmdFile] = CmdFile(name="stderr", max=4 * 1024)
cpu_limit: int = Field(0, serialization_alias="cpuLimit") cpu_limit: int = Field(4 * 1000000000, serialization_alias="cpuLimit")
real_cpu_limit: int = Field(0, serialization_alias="realCpuLimit") real_cpu_limit: int = Field(0, serialization_alias="realCpuLimit")
clock_limit: int = Field(0, serialization_alias="clockLimit") clock_limit: int = Field(8 * 1000000000, serialization_alias="clockLimit")
memory_limit: int = Field(0, serialization_alias="memoryLimit") memory_limit: int = Field(4 * 1024 * 1024, serialization_alias="memoryLimit")
stack_limit: int = Field(0, serialization_alias="stackLimit") stack_limit: int = Field(0, serialization_alias="stackLimit")
proc_limit: int = Field(0, serialization_alias="procLimit") proc_limit: int = Field(50, serialization_alias="procLimit")
cpu_rate_limit: int = Field(0, serialization_alias="cpuRateLimit") cpu_rate_limit: int = Field(0, serialization_alias="cpuRateLimit")
cpu_set_limit: str = Field("", serialization_alias="cpuSetLimit") cpu_set_limit: str = Field("", serialization_alias="cpuSetLimit")
copy_in: Dict[str, CmdFile] = Field({}, serialization_alias="copyIn") copy_in: Dict[str, CmdFile] = Field({}, serialization_alias="copyIn")
@ -44,17 +44,19 @@ class Cmd(BaseModel):
class OptionalCmd(BaseModel): class OptionalCmd(BaseModel):
args: Optional[List[str]] = None args: Optional[list[str]] = None
env: Optional[List[str]] = None env: Optional[list[str]] = ["PATH=/usr/bin:/bin:/usr/local/bin"]
stdin: Optional[CmdFile] = None stdin: Optional[CmdFile] = None
stdout: Optional[CmdFile] = None stdout: Optional[CmdFile] = None
stderr: Optional[CmdFile] = None stderr: Optional[CmdFile] = None
cpu_limit: Optional[int] = Field(None, serialization_alias="cpuLimit") cpu_limit: Optional[int] = Field(4 * 1000000000, serialization_alias="cpuLimit")
real_cpu_limit: Optional[int] = Field(None, serialization_alias="realCpuLimit") real_cpu_limit: Optional[int] = Field(None, serialization_alias="realCpuLimit")
clock_limit: Optional[int] = Field(None, serialization_alias="clockLimit") clock_limit: Optional[int] = Field(8 * 1000000000, serialization_alias="clockLimit")
memory_limit: Optional[int] = Field(None, serialization_alias="memoryLimit") memory_limit: Optional[int] = Field(
4 * 1024 * 1024, serialization_alias="memoryLimit"
)
stack_limit: Optional[int] = Field(None, serialization_alias="stackLimit") stack_limit: Optional[int] = Field(None, serialization_alias="stackLimit")
proc_limit: Optional[int] = Field(None, serialization_alias="procLimit") proc_limit: Optional[int] = Field(50, serialization_alias="procLimit")
cpu_rate_limit: Optional[int] = Field(None, serialization_alias="cpuRateLimit") cpu_rate_limit: Optional[int] = Field(None, serialization_alias="cpuRateLimit")
cpu_set_limit: Optional[str] = Field(None, serialization_alias="cpuSetLimit") cpu_set_limit: Optional[str] = Field(None, serialization_alias="cpuSetLimit")
copy_in: Optional[Dict[str, CmdFile]] = Field(None, serialization_alias="copyIn") copy_in: Optional[Dict[str, CmdFile]] = Field(None, serialization_alias="copyIn")
@ -97,7 +99,7 @@ class Parser(BaseModel):
class StageDetail(BaseModel): class StageDetail(BaseModel):
name: str name: str
group: str group: Optional[str] = ""
executor: Executor executor: Executor
parsers: List[Parser] parsers: List[Parser]

View File

@ -1,30 +1,84 @@
from datetime import datetime from datetime import datetime
from typing import List, Optional from typing import Any, Dict, List, Optional, Type
from pydantic import BaseModel, Field from pydantic import BaseModel, Field, root_validator
class ParserResultDetail(BaseModel): class ParserResultDetail(BaseModel):
time: bool = True # Display run time time: Optional[bool] = True # Display run time
mem: bool = True # Display memory usage mem: Optional[bool] = True # Display memory usage
stdout: bool = False # Display stdout messages stdout: Optional[bool] = False # Display stdout messages
stderr: bool = False # Display stderr messages stderr: Optional[bool] = False # Display stderr messages
exitstatus: Optional[bool] = False
class ParserDummy(BaseModel):
comment: Optional[str] = ""
score: Optional[int] = 0
forcequit: Optional[bool] = True
class ParserKeyword(BaseModel):
keyword: Optional[list[str]] = []
weight: Optional[list[int]] = []
class Outputs(BaseModel):
score: Optional[int] = 0
ignorespaces: Optional[bool] = False
hide: Optional[bool] = False
forcequit: Optional[bool] = True
class ParserDiff(BaseModel):
output: Optional[Outputs] = Outputs()
class Files(BaseModel): class Files(BaseModel):
import_: List[str] = Field(serialization_alias="import", validation_alias="import") import_: Optional[List[str]] = Field(
export: List[str] [], serialization_alias="import", validation_alias="import"
)
export: Optional[List[str]] = []
class Limit(BaseModel):
mem: Optional[int] = 4
cpu: Optional[int] = 4
stderr: Optional[int] = 4
stdout: Optional[int] = 4
class Stage(BaseModel): class Stage(BaseModel):
name: str # Stage name name: Optional[str] = None # Stage name
command: str # Command to run command: Optional[str] = None # Command to run
files: Files # Files to import and export files: Optional[Files] = None
score: int # Score for the task score: Optional[int] = 0
parsers: List[str] # list of parsers parsers: Optional[list[str]] = [] # list of parsers
result_detail: ParserResultDetail = ( limit: Optional[Limit] = Limit()
ParserResultDetail() dummy: Optional[ParserDummy] = ParserDummy()
) # for result-detail parser result_status: Optional[ParserDummy] = Field(ParserDummy(), alias="result-status")
keyword: Optional[ParserKeyword] = ParserKeyword()
clangtidy: Optional[ParserKeyword] = ParserKeyword()
cppcheck: Optional[ParserKeyword] = ParserKeyword()
# FIXME: determine cpplint type
cpplint: Optional[ParserKeyword] = ParserKeyword()
result_detail: Optional[ParserResultDetail] = Field(
ParserResultDetail(), alias="result-detail"
)
skip: Optional[list[str]] = []
diff: Optional[ParserDiff] = ParserDiff()
cases: Optional[Dict[str, "Stage"]] = {}
class Config:
extra = "allow"
@root_validator(pre=True)
def gather_cases(cls: Type["Stage"], values: Dict[str, Any]) -> Dict[str, Any]:
cases = {k: v for k, v in values.items() if k.startswith("case")}
for key in cases:
values.pop(key)
values["cases"] = {k: Stage(**v) for k, v in cases.items()}
return values
class Release(BaseModel): class Release(BaseModel):

1327
tests/basic/task.json Normal file

File diff suppressed because it is too large Load Diff

119
tests/basic/task.toml Normal file
View File

@ -0,0 +1,119 @@
# general task configuration
task="h4 ex1" # 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 = false
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.ignorespaces = false
case6.diff.output.hide = true

View File

@ -7,4 +7,4 @@ sandbox_token = "test"
whitelist_patterns = ["*.py", "*.txt", "*.md"] whitelist_patterns = ["*.py", "*.txt", "*.md"]
whitelist_file = ".whitelist" whitelist_file = ".whitelist"
required = ["main.py", "README.md"] required = ["main.py", "README.md"]
immutable = ["config.yaml", "setup.py"] immutable = []

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +1,124 @@
task = "hw3 ex5" # general task configuration
task="Homework 1 exercise 2" # task name
[release] release.deadline = 2024-10-12 23:59:00+08:00
deadline = "2024-10-18T23:59:00+08:00" release.stages = [ "compile" ]
[[stages]] [[stages]]
name = "judge_base" name = "Compilation"
command = "./matlab-joj ./h3/ex5.m" command = "make.sh" # eg. script running cmake commands
score = 100 files.import = [ "tools/make.sh", "src/main.c", "src/task.h", "srcCMakelist.txt" ]
parsers = ["diff", "result-detail"] files.export = [ "driver", "p2", "p2-msan" ]
limit.cpu = 180 # p2 takes long to compile
limit.stderr = 128
files.import = ["tools/matlab-joj", "tools/matlab_formatter.py"] # compile parsers
files.export = ["output/ex5_results.txt", "output/ex5_logs.txt"] parsers = [ "result-detail", "dummy", "result-status" ]
result-status.comment = "Congratulations! Your code compiled successfully."
result_detail.time = false result-status.score = 1
result_detail.mem = false result-status.forcequit = false
result_detail.stderr = true dummy.comment = "\n\n### Details\n"
result-detail.exitstatus = true
result-detail.stderr = true
result-detail.time = false
result-detail.mem = false
[[stages]] [[stages]]
name = "judge_base2" name = "File length check"
command = "./matlab-joj ./h3/ex5.m" command = "./file-length 500 400 *.c *.h" # command to run
score = 80 files.import = [ "tools/file-length" ]
parsers = ["diff", "result-detail"]
files.import = ["tools/matlab-joj", "tools/matlab_formatter.py"] parsers = [ "keyword", "dummy", "result-detail" ]
files.export = ["output/ex5_results2.txt", "output/ex5_logs2.txt"] 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
result-status.comment = "Manuel Charlemagne"
result-status.score = 10000
result-status.forcequit = true
result_detail.time = true [[stages]]
result_detail.mem = true name = "Clang-tidy checks"
result_detail.stderr = false 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.ignorespaces = false
case6.diff.output.hide = true

33
tests/immutable_file/.gitattributes vendored Normal file
View File

@ -0,0 +1,33 @@
*.avi filter=lfs diff=lfs merge=lfs -text
*.bz2 filter=lfs diff=lfs merge=lfs -text
*.djvu filter=lfs diff=lfs merge=lfs -text
*.doc filter=lfs diff=lfs merge=lfs -text
*.docx filter=lfs diff=lfs merge=lfs -text
*.epub filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.ipynb filter=lfs diff=lfs merge=lfs -text
*.jpeg filter=lfs diff=lfs merge=lfs -text
*.JPEG filter=lfs diff=lfs merge=lfs -text
*.jpg filter=lfs diff=lfs merge=lfs -text
*.JPG filter=lfs diff=lfs merge=lfs -text
*.mkv filter=lfs diff=lfs merge=lfs -text
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.ods filter=lfs diff=lfs merge=lfs -text
*.odt filter=lfs diff=lfs merge=lfs -text
*.otf filter=lfs diff=lfs merge=lfs -text
*.pdf filter=lfs diff=lfs merge=lfs -text
*.PDF filter=lfs diff=lfs merge=lfs -text
*.png filter=lfs diff=lfs merge=lfs -text
*.PNG filter=lfs diff=lfs merge=lfs -text
*.ppt filter=lfs diff=lfs merge=lfs -text
*.pptx filter=lfs diff=lfs merge=lfs -text
*.ps filter=lfs diff=lfs merge=lfs -text
*.rar filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.tgz filter=lfs diff=lfs merge=lfs -text
*.ttf filter=lfs diff=lfs merge=lfs -text
*.webm filter=lfs diff=lfs merge=lfs -text
*.xls filter=lfs diff=lfs merge=lfs -text
*.xlsx filter=lfs diff=lfs merge=lfs -text
*.xz filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

23
tests/immutable_file/.gitignore vendored Normal file
View File

@ -0,0 +1,23 @@
################################
## White list based gitignore ##
################################
# forbidden
*
.*
# allowed
!.gitignore
!.gitattributes
!.gitea/
!.gitea/issue_template/
!.gitea/workflows/
!*.yaml
!Makefile
!CMakeLists.txt
!h[0-8]/
!*.m
!*.c
!*.cpp
!*.h
!*.md

View File

@ -0,0 +1,18 @@
name: Run JOJ3 on Push
on: [push]
jobs:
run:
container:
image: focs.ji.sjtu.edu.cn:5000/gitea/runner-images:focs-ubuntu-latest-slim
volumes:
- /home/tt/.config:/home/tt/.config
- /home/tt/.cache:/home/tt/.cache
- /home/tt/.ssh:/home/tt/.ssh
steps:
- name: Check out repository code
uses: https://gitea.com/BoYanZh/checkout@focs
with:
fetch-depth: 0
- name: run joj3
run: |
sudo -E -u tt joj3 -conf-root /home/tt/.config/joj

View File

@ -0,0 +1,20 @@
name: Run JOJ3 on Release
on:
release:
types: [published]
jobs:
run:
container:
image: focs.ji.sjtu.edu.cn:5000/gitea/runner-images:focs-ubuntu-latest-slim
volumes:
- /home/tt/.config:/home/tt/.config
- /home/tt/.cache:/home/tt/.cache
- /home/tt/.ssh:/home/tt/.ssh
steps:
- name: Check out repository code
uses: https://gitea.com/BoYanZh/checkout@focs
with:
fetch-depth: 0
- name: run joj3
run: |
sudo -E -u tt joj3 -conf-root /home/tt/.config/joj -msg "feat(h1-release): joj on ${{ github.ref }}"