feat(config): document # @required for host config management (v1.0.115)
All checks were successful
技能自动化发布 / release (push) Successful in 6s

This commit is contained in:
2026-09-25 21:51:05 +08:00
parent e32d04ec50
commit dfe6ade2d8
8 changed files with 98 additions and 2 deletions

View File

@@ -30,6 +30,7 @@ POLICY_IDS = (
"POLICY-CONFIG-002",
"POLICY-CONFIG-003",
"POLICY-CONFIG-004",
"POLICY-CONFIG-005",
"POLICY-TESTING-001",
"POLICY-TESTING-002",
"POLICY-RPA-001",
@@ -96,8 +97,11 @@ CONFIG_ENV_FORBIDDEN_PATTERNS = (
)
CONFIG_004_SOURCE = "development/CONFIG.md §.env.example 注释 = 配置管理用户文案"
CONFIG_005_SOURCE = "development/CONFIG.md §必填项:# @required"
CONFIG_004_ENV_KEY_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
CONFIG_004_CJK_RE = re.compile(r"[\u4e00-\u9fff]")
CONFIG_005_REQUIRED_MARKER_RE = re.compile(r"^@required\b", re.IGNORECASE)
CONFIG_005_INLINE_REQUIRED_RE = re.compile(r"@required\b", re.IGNORECASE)
# 注释中禁止出现的开发向指向 / 黑话(大小写不敏感子串)
CONFIG_004_FORBIDDEN_COMMENT_SUBSTRINGS = (
"development/",
@@ -316,6 +320,34 @@ def _config_004_comment_offenders(path: str) -> list[str]:
return offenders
def _is_env_required_marker(text: str) -> bool:
return bool(CONFIG_005_REQUIRED_MARKER_RE.match(text.strip()))
def _config_005_offenders(path: str) -> list[str]:
offenders: list[str] = []
for lineno, key, _value, headers, inline in _iter_env_example_active_entries(path):
if inline and CONFIG_005_INLINE_REQUIRED_RE.search(inline):
offenders.append(
f".env.example:{lineno}: {key}= 禁止在行尾写 @required(须单独一行放在中文行头上方)"
)
required_markers = [part for part in headers if _is_env_required_marker(part)]
non_marker_headers = [part for part in headers if not _is_env_required_marker(part)]
if not required_markers:
continue
if not non_marker_headers:
offenders.append(
f".env.example:{lineno}: {key}= 使用了 # @required 但缺少中文行头"
)
continue
if not any(CONFIG_004_CJK_RE.search(part) for part in non_marker_headers):
offenders.append(
f".env.example:{lineno}: {key}= # @required 下方须有中文行头(说明「是什么」)"
)
return offenders
def _rel(skill_root: str, path: str) -> str:
return os.path.relpath(path, skill_root).replace("\\", "/")
@@ -575,6 +607,23 @@ class TestPolicyConfig004(unittest.TestCase):
)
class TestPolicyConfig005(unittest.TestCase):
def test_env_example_required_markers_are_well_formed(self) -> None:
path = os.path.join(get_skill_root(), ".env.example")
self.assertTrue(os.path.isfile(path), msg=".env.example missing")
offenders = _config_005_offenders(path)
self.assertEqual(
offenders,
[],
msg=_policy_msg(
"POLICY-CONFIG-005",
CONFIG_005_SOURCE,
"\n".join(offenders)
or "every # @required marker needs its own Chinese header line above the key",
),
)
class TestPolicyTesting001(unittest.TestCase):
def test_root_tests_do_not_hardcode_real_targets(self) -> None:
skill_root = get_skill_root()