chore: auto release commit (2026-07-11 15:57:40)
All checks were successful
技能自动化发布 / release (push) Successful in 5s
All checks were successful
技能自动化发布 / release (push) Successful in 5s
This commit is contained in:
@@ -32,6 +32,7 @@ POLICY_IDS = (
|
||||
"POLICY-TESTING-002",
|
||||
"POLICY-RPA-001",
|
||||
"POLICY-RPA-002",
|
||||
"POLICY-RPA-003",
|
||||
"POLICY-PACKAGING-001",
|
||||
"POLICY-PACKAGING-002",
|
||||
"POLICY-DOCS-001",
|
||||
@@ -109,6 +110,10 @@ RPA_002_FFMPEG_PATTERNS = (
|
||||
|
||||
RPA_002_EXEMPT_REL = "scripts/service/task_run_support.py"
|
||||
|
||||
RPA_003_SOURCE = (
|
||||
"development/RPA.md §5.3; development/TESTING.md §12; development/POLICY_MATRIX.md"
|
||||
)
|
||||
|
||||
LOGGING_001_SOURCE = "development/LOGGING.md; development/RUNTIME.md"
|
||||
LOGGING_002_SOURCE = "development/LOGGING.md; development/DEVELOPMENT.md"
|
||||
LOGGING_003_SOURCE = "development/LOGGING.md"
|
||||
@@ -470,6 +475,30 @@ class TestPolicyRpa002(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPolicyRpa003(unittest.TestCase):
|
||||
def test_service_layer_retains_video_session_integration(self) -> None:
|
||||
skill_root = get_skill_root()
|
||||
service_files = _walk_files(skill_root, "scripts/service", suffix=".py")
|
||||
self.assertTrue(service_files, msg="scripts/service/*.py not found")
|
||||
combined = "\n".join(_read_text(path) for path in service_files)
|
||||
missing: list[str] = []
|
||||
if "RpaVideoSession" not in combined:
|
||||
missing.append("RpaVideoSession")
|
||||
if "video.add_step" not in combined and ".add_step(" not in combined:
|
||||
missing.append("video.add_step or .add_step(")
|
||||
if "merge_video_into_result_summary" not in combined:
|
||||
missing.append("merge_video_into_result_summary")
|
||||
self.assertEqual(
|
||||
missing,
|
||||
[],
|
||||
msg=_policy_msg(
|
||||
"POLICY-RPA-003",
|
||||
RPA_003_SOURCE,
|
||||
"scripts/service/*.py missing: " + ", ".join(missing),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _scan_sensitive_logging_assignments(skill_root: str) -> list[str]:
|
||||
offenders: list[str] = []
|
||||
for path in _walk_files(skill_root, "scripts", suffix=".py"):
|
||||
|
||||
@@ -15,6 +15,46 @@ from _support import IsolatedDataRoot, get_skill_root
|
||||
from jiangchang_skill_core import config
|
||||
|
||||
|
||||
def _active_env_value(text: str, key: str) -> str | None:
|
||||
"""Return the active (non-comment) KEY=value line from .env-style text."""
|
||||
prefix = f"{key}="
|
||||
for line in text.splitlines():
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
active = stripped.split("#", 1)[0].strip()
|
||||
if active.startswith(prefix):
|
||||
return active
|
||||
return None
|
||||
|
||||
|
||||
def _combined_service_source(skill_root: str | None = None) -> str:
|
||||
"""Merge scripts/service/*.py source (excludes __pycache__)."""
|
||||
root = skill_root or get_skill_root()
|
||||
service_dir = os.path.join(root, "scripts", "service")
|
||||
parts: list[str] = []
|
||||
for dirpath, dirnames, filenames in os.walk(service_dir):
|
||||
dirnames[:] = [d for d in dirnames if d != "__pycache__"]
|
||||
for name in sorted(filenames):
|
||||
if not name.endswith(".py"):
|
||||
continue
|
||||
path = os.path.join(dirpath, name)
|
||||
with open(path, encoding="utf-8") as f:
|
||||
parts.append(f.read())
|
||||
return "\n".join(parts)
|
||||
|
||||
|
||||
def _missing_video_integration_tokens(service_text: str) -> list[str]:
|
||||
missing: list[str] = []
|
||||
if "RpaVideoSession" not in service_text:
|
||||
missing.append("RpaVideoSession")
|
||||
if "video.add_step" not in service_text and ".add_step(" not in service_text:
|
||||
missing.append("video.add_step or .add_step(")
|
||||
if "merge_video_into_result_summary" not in service_text:
|
||||
missing.append("merge_video_into_result_summary")
|
||||
return missing
|
||||
|
||||
|
||||
class TestEnvExampleVideoDefaults(unittest.TestCase):
|
||||
def test_env_example_contains_required_keys(self) -> None:
|
||||
path = os.path.join(get_skill_root(), ".env.example")
|
||||
@@ -22,13 +62,45 @@ class TestEnvExampleVideoDefaults(unittest.TestCase):
|
||||
text = f.read()
|
||||
for key in (
|
||||
"OPENCLAW_" + "TEST_TARGET=mock",
|
||||
"OPENCLAW_RECORD_VIDEO=1",
|
||||
"OPENCLAW_RECORD_VIDEO=0",
|
||||
"OPENCLAW_ARTIFACTS_ON_FAILURE=1",
|
||||
"OPENCLAW_BROWSER_HEADLESS=0",
|
||||
"OPENCLAW_PLAYWRIGHT_STEALTH=1",
|
||||
):
|
||||
self.assertIn(key, text, msg=f"missing {key!r} in .env.example")
|
||||
|
||||
def test_env_example_default_off_but_template_keeps_video_integration(self) -> None:
|
||||
path = os.path.join(get_skill_root(), ".env.example")
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
self.assertEqual(
|
||||
_active_env_value(text, "OPENCLAW_RECORD_VIDEO"),
|
||||
"OPENCLAW_RECORD_VIDEO=0",
|
||||
)
|
||||
|
||||
service_text = _combined_service_source()
|
||||
missing = _missing_video_integration_tokens(service_text)
|
||||
self.assertEqual(
|
||||
missing,
|
||||
[],
|
||||
msg=(
|
||||
"default OPENCLAW_RECORD_VIDEO=0 does not allow removing video integration; "
|
||||
f"scripts/service/*.py missing: {', '.join(missing)}"
|
||||
),
|
||||
)
|
||||
|
||||
def test_env_example_allows_record_video_one_in_comments_only(self) -> None:
|
||||
sample = "\n".join(
|
||||
[
|
||||
"# example: OPENCLAW_RECORD_VIDEO=1 for production",
|
||||
"OPENCLAW_RECORD_VIDEO=0",
|
||||
]
|
||||
)
|
||||
self.assertEqual(
|
||||
_active_env_value(sample, "OPENCLAW_RECORD_VIDEO"),
|
||||
"OPENCLAW_RECORD_VIDEO=0",
|
||||
)
|
||||
|
||||
|
||||
class TestPrintVideoSummary(unittest.TestCase):
|
||||
def test_cli_prints_video_path_when_enabled(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user