diff --git a/joj3_config_generator/models/repo.py b/joj3_config_generator/models/repo.py index 20cf756..eeb99be 100644 --- a/joj3_config_generator/models/repo.py +++ b/joj3_config_generator/models/repo.py @@ -1,8 +1,10 @@ import os from pathlib import Path -from typing import List +from typing import Any, List -from pydantic import AliasChoices, BaseModel, Field, model_validator +from pydantic import AliasChoices, BaseModel, Field, field_validator, model_validator + +from joj3_config_generator.models.common import Memory class Files(BaseModel): @@ -34,6 +36,24 @@ class Issue(BaseModel): ) +class HealthCheck(BaseModel): + score: int = 0 + max_size: int = Field( + Memory("10m"), validation_alias=AliasChoices("max-size", "max_size") + ) + immutable_path: Path = Field( + Path("immutable"), + validation_alias=AliasChoices("immutable-path", "immutable_path"), + ) + + @field_validator("max_size", mode="before") + @classmethod + def ensure_mem_type(cls, v: Any) -> Memory: + if isinstance(v, str): + return Memory(v) + raise ValueError(f'Must be a string, e.g., "256m" or "1g", but got {v}') + + class Config(BaseModel): root: Path = Field(Path("."), exclude=True) path: Path = Field(Path("repo.toml"), exclude=True) @@ -52,9 +72,9 @@ class Config(BaseModel): ), exclude=True, ) - - max_size: float = Field( - 10, ge=0, validation_alias=AliasChoices("max-size", "max_size") + grading_repo_name: str = Field( + "", + validation_alias=AliasChoices("grading-repo-name", "grading_repo_name"), ) files: Files = Files() sandbox_token: str = Field( @@ -64,18 +84,23 @@ class Config(BaseModel): 100, validation_alias=AliasChoices("max-total-score", "max_total_score") ) groups: Groups = Groups() + issue: Issue = Issue() + + health_check: HealthCheck = Field( + HealthCheck(), validation_alias=AliasChoices("health-check", "health_check") + ) + # TODO: remove max_size, health_check_score, and immutable_path in the future + max_size: float = Field( + 10, ge=0, validation_alias=AliasChoices("max-size", "max_size") + ) health_check_score: int = Field( 0, validation_alias=AliasChoices("health-check-score", "health_check_score") ) - issue: Issue = Issue() immutable_path: Path = Field( Path("immutable"), validation_alias=AliasChoices("immutable-path", "immutable_path"), ) - grading_repo_name: str = Field( - "", - validation_alias=AliasChoices("grading-repo-name", "grading_repo_name"), - ) + # TODO: remove gitea_token and gitea_org in the future gitea_token: str = Field( "", validation_alias=AliasChoices("gitea-token", "gitea_token") @@ -91,3 +116,14 @@ class Config(BaseModel): else: self.grading_repo_name = Path.cwd().name return self + + # TODO: remove this validator in the future + @model_validator(mode="after") + def set_health_check(self) -> "Config": + if "health_check_score" in self.model_fields_set: + self.health_check.score = self.health_check_score + if "max_size" in self.model_fields_set: + self.health_check.max_size = Memory(f"{self.max_size}m") + if "immutable_path" in self.model_fields_set: + self.health_check.immutable_path = self.immutable_path + return self diff --git a/joj3_config_generator/transformers/repo.py b/joj3_config_generator/transformers/repo.py index 18e254a..4765a3e 100644 --- a/joj3_config_generator/transformers/repo.py +++ b/joj3_config_generator/transformers/repo.py @@ -101,7 +101,9 @@ def get_check_lists(repo_conf: repo.Config) -> Tuple[List[str], List[str]]: immutable_files.append(file_path) file_sums.append(calc_sha256sum(file_path)) file_names.append(file) - immutable_dir = (repo_conf.root / repo_conf.path).parent / repo_conf.immutable_path + immutable_dir = ( + repo_conf.root / repo_conf.path + ).parent / repo_conf.health_check.immutable_path if not immutable_dir.exists(): return file_sums, file_names file_sums = [] @@ -122,7 +124,7 @@ def get_health_check_args(repo_conf: repo.Config) -> List[str]: return [ "/usr/local/bin/repo-health-checker", "-root=.", - f"-repoSize={str(repo_conf.max_size)}", + f"-repoSize={str(repo_conf.health_check.max_size / 1024 / 1024)}", # B -> MB *[f"-meta={meta}" for meta in repo_conf.files.required], f"-checkFileSumList={','.join(file_sums)}", f"-checkFileNameList={','.join(file_names)}", @@ -194,7 +196,7 @@ def get_health_check_stage( parsers=[ result.Parser( name="healthcheck", - with_=result.ScoreConfig(score=repo_conf.health_check_score), + with_=result.ScoreConfig(score=repo_conf.health_check.score), ), result.Parser(name="debug"), ], diff --git a/tests/convert/basic/repo.toml b/tests/convert/basic/repo.toml index cf8d457..616456b 100644 --- a/tests/convert/basic/repo.toml +++ b/tests/convert/basic/repo.toml @@ -3,9 +3,9 @@ grading-repo-name = "ece280-joj" sandbox-token = "test" # reconfigure later max-total-score = 1000 -max-size = 50.5 -immutable_path = "immutable" +health-check.max-size = "50.5m" +health-check.immutable_path = "immutable" # for tests [groups] diff --git a/tests/convert/full/repo.toml b/tests/convert/full/repo.toml new file mode 100644 index 0000000..bbbf21c --- /dev/null +++ b/tests/convert/full/repo.toml @@ -0,0 +1,23 @@ +files.required = ["README.md", "Changelog.md"] # required files name, case insensitive +sandbox-token = "" # sandbox token + +health-check.score = 0 # score for health check stage +health-check.max-size = "10m" # max size of the repository +health-check.immutable-path = "immutable" # path for immutable files, relative to the path of repo.toml + +issue.label.name = "Kind/Testing" # label for issues +issue.label.color = "#795548" # color for the label +issue.label.exclusive = false # whether the label is exclusive +issue.show-submitter = true # whether to show submitter in the issue title + + +# fields below can be overridden by task.toml +max-total-score = 100 # maximum total score for the task +# submission count limit groups +# explanation of the following config: +# in last 1 hour, total submission <= 50 times +# in last 24 hours, submission includes group "joj" <= 1000 times +# in last 2 hours, submission includes group "run" <= 100 times +groups.name = ["", "joj", "run"] # names of the groups +groups.max-count = [50, 1000, 100] # maximum submission count for each group +groups.time-period-hour = [1, 24, 2] # time period in hour for each group