Release v1.0.50: web RPA profile/headed hard rules (POLICY-RPA-004)
All checks were successful
技能自动化发布 / release (push) Successful in 17s
All checks were successful
技能自动化发布 / release (push) Successful in 17s
This commit is contained in:
@@ -35,6 +35,7 @@ POLICY_IDS = (
|
||||
"POLICY-RPA-001",
|
||||
"POLICY-RPA-002",
|
||||
"POLICY-RPA-003",
|
||||
"POLICY-RPA-004",
|
||||
"POLICY-PACKAGING-001",
|
||||
"POLICY-PACKAGING-002",
|
||||
"POLICY-DOCS-001",
|
||||
@@ -142,6 +143,18 @@ RPA_003_SOURCE = (
|
||||
"development/RPA.md §5.3; development/TESTING.md §12; development/POLICY_MATRIX.md"
|
||||
)
|
||||
|
||||
RPA_004_SOURCE = (
|
||||
"development/RPA.md §0.2; development/ADAPTER.md §兄弟依赖; development/POLICY_MATRIX.md"
|
||||
)
|
||||
RPA_004_HEADLESS_KEY_RE = re.compile(
|
||||
r"(?m)^\s*OPENCLAW_BROWSER_HEADLESS\s*=\s*([^\s#]+)"
|
||||
)
|
||||
RPA_004_LAUNCH_MARKERS = (
|
||||
"launch_persistent_context",
|
||||
"launch_persistent_browser",
|
||||
)
|
||||
RPA_004_PICK_MARKER = "pick_web_account"
|
||||
|
||||
LOGGING_001_SOURCE = "development/LOGGING.md; development/RUNTIME.md"
|
||||
LOGGING_002_SOURCE = "development/LOGGING.md; development/DEVELOPMENT.md"
|
||||
LOGGING_003_SOURCE = "development/LOGGING.md"
|
||||
@@ -615,6 +628,47 @@ class TestPolicyRpa003(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPolicyRpa004(unittest.TestCase):
|
||||
def test_env_example_browser_headless_defaults_to_headed(self) -> None:
|
||||
skill_root = get_skill_root()
|
||||
env_path = os.path.join(skill_root, ".env.example")
|
||||
self.assertTrue(os.path.isfile(env_path), msg=".env.example missing")
|
||||
text = _read_text(env_path)
|
||||
match = RPA_004_HEADLESS_KEY_RE.search(text)
|
||||
if match is None:
|
||||
return
|
||||
value = match.group(1).strip().strip("\"'")
|
||||
self.assertEqual(
|
||||
value,
|
||||
"0",
|
||||
msg=_policy_msg(
|
||||
"POLICY-RPA-004",
|
||||
RPA_004_SOURCE,
|
||||
f".env.example OPENCLAW_BROWSER_HEADLESS must default to 0 (headed), got {value!r}",
|
||||
),
|
||||
)
|
||||
|
||||
def test_launch_persistent_requires_pick_web_account(self) -> None:
|
||||
skill_root = get_skill_root()
|
||||
script_texts = [
|
||||
_read_text(path) for path in _walk_files(skill_root, "scripts", suffix=".py")
|
||||
]
|
||||
combined = "\n".join(script_texts)
|
||||
uses_launch = any(marker in combined for marker in RPA_004_LAUNCH_MARKERS)
|
||||
if not uses_launch:
|
||||
return
|
||||
self.assertIn(
|
||||
RPA_004_PICK_MARKER,
|
||||
combined,
|
||||
msg=_policy_msg(
|
||||
"POLICY-RPA-004",
|
||||
RPA_004_SOURCE,
|
||||
"scripts/ uses launch_persistent_* but missing pick_web_account "
|
||||
"(must obtain profile_dir before opening browser)",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def _scan_sensitive_logging_assignments(skill_root: str) -> list[str]:
|
||||
offenders: list[str] = []
|
||||
for path in _walk_files(skill_root, "scripts", suffix=".py"):
|
||||
|
||||
@@ -238,6 +238,17 @@ class TestDocsStandards(unittest.TestCase):
|
||||
msg="RPA.md must mention jc2009 / industry simulator / portal gate",
|
||||
)
|
||||
|
||||
def test_rpa_md_covers_web_profile_headed_and_login_split(self) -> None:
|
||||
text = self._read("development/RPA.md")
|
||||
self.assertIn("0.2", text)
|
||||
self.assertIn("profile_dir", text)
|
||||
self.assertIn("POLICY-RPA-004", text)
|
||||
self.assertIn("有头", text)
|
||||
self.assertTrue(
|
||||
"登录不全局强制" in text or "登录 ≠ Profile" in text or "登录≠Profile" in text,
|
||||
msg="RPA.md §0.2 must separate site-login from Profile requirement",
|
||||
)
|
||||
|
||||
def test_simulator_example_readme_covers_async_and_account_manager(self) -> None:
|
||||
text = self._read("examples/simulator_browser_rpa/README.md")
|
||||
self.assertIn("async", text.lower())
|
||||
|
||||
Reference in New Issue
Block a user