chore: auto release commit (2026-07-03 18:50:07)
All checks were successful
技能自动化发布 / release (push) Successful in 6s
All checks were successful
技能自动化发布 / release (push) Successful in 6s
This commit is contained in:
@@ -174,6 +174,84 @@ class TestDocsStandards(unittest.TestCase):
|
||||
self.assertIn("RpaVideoSession", text)
|
||||
self.assertIn("1.0.17", text)
|
||||
|
||||
def test_rpa_md_forbids_rpa_helpers_import(self) -> None:
|
||||
text = self._read("development/RPA.md")
|
||||
self.assertIn("rpa_helpers", text)
|
||||
self.assertTrue(
|
||||
"不要 import" in text or "不要" in text and "import" in text,
|
||||
msg="RPA.md must document forbidding rpa_helpers import",
|
||||
)
|
||||
|
||||
def test_rpa_md_covers_simulator_playwright_layering(self) -> None:
|
||||
text = self._read("development/RPA.md")
|
||||
self.assertTrue(
|
||||
"simulator_playwright" in text
|
||||
or ("薄 adapter" in text and "playwright.py" in text),
|
||||
msg="RPA.md must describe thin adapter + *_playwright.py layering",
|
||||
)
|
||||
|
||||
def test_rpa_md_covers_industry_simulator_portal(self) -> None:
|
||||
text = self._read("development/RPA.md")
|
||||
self.assertTrue(
|
||||
any(marker in text for marker in ("jc2009", "行业仿真", "门户")),
|
||||
msg="RPA.md must mention jc2009 / industry simulator / portal gate",
|
||||
)
|
||||
|
||||
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())
|
||||
self.assertTrue(
|
||||
"account-manager" in text.lower() or "pick-web" in text or "pick_web_account" in text,
|
||||
msg="simulator_browser_rpa README must mention async and account-manager",
|
||||
)
|
||||
|
||||
def test_development_md_documents_scaffold_and_git_cleanup(self) -> None:
|
||||
text = self._read("development/DEVELOPMENT.md")
|
||||
self.assertTrue(
|
||||
"scaffold_skill" in text or "scaffold_skill.ps1" in text,
|
||||
msg="DEVELOPMENT.md must document scaffold_skill",
|
||||
)
|
||||
self.assertTrue(
|
||||
("Remove-Item" in text and ".git" in text)
|
||||
or ("删除" in text and ".git" in text),
|
||||
msg="DEVELOPMENT.md must document deleting .git",
|
||||
)
|
||||
|
||||
def test_naming_md_copy_flow_documents_scaffold_or_git_cleanup(self) -> None:
|
||||
text = self._read("development/NAMING.md")
|
||||
self.assertTrue(
|
||||
"scaffold" in text.lower() or ("清理" in text and ".git" in text),
|
||||
msg="NAMING.md §9 must mention scaffold or Git cleanup",
|
||||
)
|
||||
|
||||
def test_scaffold_ps1_exists(self) -> None:
|
||||
path = os.path.join(get_skill_root(), "tools", "scaffold_skill.ps1")
|
||||
self.assertTrue(os.path.isfile(path), msg="tools/scaffold_skill.ps1 must exist")
|
||||
|
||||
def test_guarded_docs_do_not_reference_amazon_implementation(self) -> None:
|
||||
forbidden = (
|
||||
"download-settlement-report-amazon",
|
||||
"amazon_sim",
|
||||
"#amz-email",
|
||||
)
|
||||
guarded_docs = (
|
||||
"development/RPA.md",
|
||||
"development/ADAPTER.md",
|
||||
"development/DEVELOPMENT.md",
|
||||
"development/CONFIG.md",
|
||||
"development/TESTING.md",
|
||||
"examples/README.md",
|
||||
"examples/simulator_browser_rpa/README.md",
|
||||
)
|
||||
for rel in guarded_docs:
|
||||
text = self._read(rel)
|
||||
for fragment in forbidden:
|
||||
self.assertNotIn(
|
||||
fragment,
|
||||
text,
|
||||
msg=f"{rel} must not reference Amazon implementation {fragment!r}",
|
||||
)
|
||||
|
||||
def test_adapter_md_covers_four_tiers_and_config_get(self) -> None:
|
||||
text = self._read("development/ADAPTER.md")
|
||||
for marker in (
|
||||
|
||||
39
tests/test_no_rpa_helpers_import.py
Normal file
39
tests/test_no_rpa_helpers_import.py
Normal file
@@ -0,0 +1,39 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""禁止在模板主骨架 scripts/ 中 import account-manager 内部 RPA 辅助模块。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
import unittest
|
||||
|
||||
from _support import get_skill_root
|
||||
|
||||
FORBIDDEN_PATTERNS = (
|
||||
re.compile(r"\brpa_helpers\b"),
|
||||
re.compile(r"\binject_account_manager_scripts_path\b"),
|
||||
re.compile(r"\bget_account_credential\b"),
|
||||
)
|
||||
|
||||
|
||||
class TestNoRpaHelpersImport(unittest.TestCase):
|
||||
def test_scripts_tree_has_no_forbidden_account_manager_imports(self) -> None:
|
||||
scripts_dir = os.path.join(get_skill_root(), "scripts")
|
||||
violations: list[str] = []
|
||||
for root, _dirs, files in os.walk(scripts_dir):
|
||||
for name in files:
|
||||
if not name.endswith(".py"):
|
||||
continue
|
||||
rel = os.path.relpath(os.path.join(root, name), scripts_dir)
|
||||
text = open(os.path.join(root, name), encoding="utf-8").read()
|
||||
for pattern in FORBIDDEN_PATTERNS:
|
||||
if pattern.search(text):
|
||||
violations.append(f"{rel}: {pattern.pattern}")
|
||||
self.assertEqual(
|
||||
violations,
|
||||
[],
|
||||
msg="Forbidden account-manager internal imports in scripts/:\n" + "\n".join(violations),
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
64
tests/test_scaffold_guard.py
Normal file
64
tests/test_scaffold_guard.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""skill-template 脚手架与 Git 防串库守护。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from _support import get_skill_root
|
||||
|
||||
TEMPLATE_MARKER = ".openclaw-skill-template"
|
||||
TEMPLATE_SLUG = "your-skill-slug"
|
||||
|
||||
|
||||
def _read_skill_slug() -> str:
|
||||
constants_path = os.path.join(get_skill_root(), "scripts", "util", "constants.py")
|
||||
with open(constants_path, encoding="utf-8") as f:
|
||||
for line in f:
|
||||
if line.strip().startswith("SKILL_SLUG"):
|
||||
_, _, value = line.partition("=")
|
||||
return value.strip().strip('"').strip("'")
|
||||
return ""
|
||||
|
||||
|
||||
class TestScaffoldGuard(unittest.TestCase):
|
||||
def test_template_repo_has_marker(self) -> None:
|
||||
slug = _read_skill_slug()
|
||||
if slug != TEMPLATE_SLUG:
|
||||
self.skipTest(f"当前 SKILL_SLUG={slug!r},非模板占位,跳过 template marker 断言")
|
||||
marker = os.path.join(get_skill_root(), TEMPLATE_MARKER)
|
||||
self.assertTrue(
|
||||
os.path.isfile(marker),
|
||||
msg=f"skill-template 源仓库根目录必须存在 {TEMPLATE_MARKER}",
|
||||
)
|
||||
|
||||
def test_business_skill_must_not_keep_template_marker(self) -> None:
|
||||
slug = _read_skill_slug()
|
||||
if slug == TEMPLATE_SLUG:
|
||||
self.skipTest("当前仍为模板占位 slug,由 test_template_repo_has_marker 守护")
|
||||
marker = os.path.join(get_skill_root(), TEMPLATE_MARKER)
|
||||
self.assertFalse(
|
||||
os.path.isfile(marker),
|
||||
msg=(
|
||||
"业务技能不得保留 .openclaw-skill-template;"
|
||||
"请使用 tools/scaffold_skill 或删除标记文件并检查是否误复制了模板 .git"
|
||||
),
|
||||
)
|
||||
|
||||
def test_development_md_documents_git_cleanup(self) -> None:
|
||||
path = os.path.join(get_skill_root(), "development", "DEVELOPMENT.md")
|
||||
with open(path, encoding="utf-8") as f:
|
||||
text = f.read()
|
||||
self.assertIn(".git", text)
|
||||
self.assertTrue(
|
||||
"scaffold_skill" in text or "scaffold" in text.lower(),
|
||||
msg="DEVELOPMENT.md must document scaffold_skill",
|
||||
)
|
||||
self.assertTrue(
|
||||
"remote" in text.lower() or "git remote" in text.lower(),
|
||||
msg="DEVELOPMENT.md must mention git remote self-check",
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user