chore: auto release commit (2026-07-11 15:00:19)
All checks were successful
技能自动化发布 / release (push) Successful in 5s

This commit is contained in:
2026-07-11 15:00:20 +08:00
parent dc4eecbb35
commit c67079fb9e
9 changed files with 650 additions and 33 deletions

View File

@@ -35,6 +35,11 @@ POLICY_IDS = (
"POLICY-PACKAGING-001",
"POLICY-PACKAGING-002",
"POLICY-DOCS-001",
"POLICY-LOGGING-001",
"POLICY-LOGGING-002",
"POLICY-LOGGING-003",
"POLICY-LOGGING-004",
"POLICY-LOGGING-005",
)
STRUCTURE_PATHS = (
@@ -104,6 +109,25 @@ RPA_002_FFMPEG_PATTERNS = (
RPA_002_EXEMPT_REL = "scripts/service/task_run_support.py"
LOGGING_001_SOURCE = "development/LOGGING.md; development/RUNTIME.md"
LOGGING_002_SOURCE = "development/LOGGING.md; development/DEVELOPMENT.md"
LOGGING_003_SOURCE = "development/LOGGING.md"
LOGGING_004_SOURCE = "development/LOGGING.md; development/RPA.md"
LOGGING_005_SOURCE = "development/LOGGING.md; development/CONFIG.md"
LOGGING_CALL_PATTERN = re.compile(
r"\b(log|logger|get_skill_logger\s*\(\s*\))\s*\.\s*(info|warning|error|debug|exception|critical)\s*\(",
re.IGNORECASE,
)
LOGGING_CALL_GET_LOGGER_PATTERN = re.compile(
r"get_skill_logger\s*\(\s*\)\s*\.\s*(info|warning|error|debug|exception|critical)\s*\(",
re.IGNORECASE,
)
SENSITIVE_LOG_ASSIGNMENT_PATTERN = re.compile(
r"(password|token|cookie|secret|api_key|authorization)\s*=",
re.IGNORECASE,
)
def _policy_msg(policy_id: str, source: str, detail: str) -> str:
return f"{policy_id} [{source}]: {detail}"
@@ -446,6 +470,126 @@ class TestPolicyRpa002(unittest.TestCase):
)
def _scan_sensitive_logging_assignments(skill_root: str) -> list[str]:
offenders: list[str] = []
for path in _walk_files(skill_root, "scripts", suffix=".py"):
rel = _rel(skill_root, path)
for lineno, line in enumerate(_read_text(path).splitlines(), 1):
stripped = line.strip()
if not stripped or stripped.startswith("#"):
continue
if not LOGGING_CALL_PATTERN.search(line):
continue
if SENSITIVE_LOG_ASSIGNMENT_PATTERN.search(line):
offenders.append(f"{rel}:{lineno}: {stripped}")
return offenders
class TestPolicyLogging001(unittest.TestCase):
def test_cli_app_sets_up_unified_logging(self) -> None:
skill_root = get_skill_root()
path = os.path.join(skill_root, "scripts", "cli", "app.py")
text = _read_text(path)
rel = _rel(skill_root, path)
self.assertIn(
"setup_skill_logging",
text,
msg=_policy_msg(
"POLICY-LOGGING-001",
LOGGING_001_SOURCE,
f"{rel} missing setup_skill_logging",
),
)
has_logger_info = bool(
LOGGING_CALL_GET_LOGGER_PATTERN.search(text)
or re.search(r"\bget_skill_logger\s*\(\s*\)\s*\.\s*info\s*\(", text)
)
self.assertTrue(
has_logger_info,
msg=_policy_msg(
"POLICY-LOGGING-001",
LOGGING_001_SOURCE,
f"{rel} missing get_skill_logger().info (or equivalent)",
),
)
class TestPolicyLogging002(unittest.TestCase):
def test_task_service_uses_get_skill_logger(self) -> None:
skill_root = get_skill_root()
path = os.path.join(skill_root, "scripts", "service", "task_service.py")
text = _read_text(path)
rel = _rel(skill_root, path)
self.assertRegex(
text,
r"get_skill_logger",
msg=_policy_msg(
"POLICY-LOGGING-002",
LOGGING_002_SOURCE,
f"{rel} must import/use get_skill_logger",
),
)
class TestPolicyLogging003(unittest.TestCase):
def test_task_service_logs_start_failure_and_task_logs(self) -> None:
skill_root = get_skill_root()
path = os.path.join(skill_root, "scripts", "service", "task_service.py")
text = _read_text(path)
rel = _rel(skill_root, path)
missing: list[str] = []
if not re.search(r"\b(log|logger)\.info\s*\(", text):
missing.append("log.info or logger.info")
if not re.search(r"\b(log|logger)\.exception\s*\(", text):
missing.append("log.exception or logger.exception")
if "save_task_log" not in text:
missing.append("save_task_log")
self.assertEqual(
missing,
[],
msg=_policy_msg(
"POLICY-LOGGING-003",
LOGGING_003_SOURCE,
f"{rel} missing: " + ", ".join(missing),
),
)
class TestPolicyLogging004(unittest.TestCase):
def test_task_service_emits_progress_for_long_tasks(self) -> None:
skill_root = get_skill_root()
path = os.path.join(skill_root, "scripts", "service", "task_service.py")
text = _read_text(path)
rel = _rel(skill_root, path)
has_progress = bool(
re.search(r"\bemit\s*\(", text)
or re.search(r"\bactivity\.emit\s*\(", text)
or re.search(r"\bvideo\.add_step\s*\(", text)
)
self.assertTrue(
has_progress,
msg=_policy_msg(
"POLICY-LOGGING-004",
LOGGING_004_SOURCE,
f"{rel} must use emit( / activity.emit / video.add_step",
),
)
class TestPolicyLogging005(unittest.TestCase):
def test_scripts_do_not_log_sensitive_assignments(self) -> None:
offenders = _scan_sensitive_logging_assignments(get_skill_root())
self.assertEqual(
offenders,
[],
msg=_policy_msg(
"POLICY-LOGGING-005",
LOGGING_005_SOURCE,
"\n".join(offenders),
),
)
class TestPolicyDocs001(unittest.TestCase):
def test_policy_matrix_exists_and_lists_policy_ids(self) -> None:
skill_root = get_skill_root()