Add DATA_PATHS golden standard and resolve_data_path helpers.
All checks were successful
技能自动化发布 / release (push) Successful in 5s
All checks were successful
技能自动化发布 / release (push) Successful in 5s
Document skill-owned file layout under user data dir, extend runtime_paths with standard subdir helpers, add POLICY-DATA-PATH-001, and update CONFIG/RUNTIME cross-references. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -44,6 +44,7 @@ POLICY_IDS = (
|
||||
"POLICY-CONTROL-001",
|
||||
"POLICY-CONTROL-002",
|
||||
"POLICY-CONTROL-003",
|
||||
"POLICY-DATA-PATH-001",
|
||||
)
|
||||
|
||||
STRUCTURE_PATHS = (
|
||||
@@ -681,6 +682,58 @@ class TestPolicyControl003(unittest.TestCase):
|
||||
)
|
||||
|
||||
|
||||
class TestPolicyDataPath001(unittest.TestCase):
|
||||
def test_env_example_has_no_cwd_relative_paths(self) -> None:
|
||||
path = os.path.join(get_skill_root(), ".env.example")
|
||||
offenders: list[str] = []
|
||||
with open(path, encoding="utf-8") as f:
|
||||
for lineno, line in enumerate(f, 1):
|
||||
stripped = line.strip()
|
||||
if not stripped or stripped.startswith("#"):
|
||||
continue
|
||||
active = stripped.split("#", 1)[0].strip()
|
||||
if re.search(r"=\s*\./", active):
|
||||
offenders.append(f".env.example:{lineno}: {active}")
|
||||
self.assertEqual(
|
||||
offenders,
|
||||
[],
|
||||
msg=_policy_msg(
|
||||
"POLICY-DATA-PATH-001",
|
||||
"development/DATA_PATHS.md",
|
||||
"\n".join(offenders) or "CWD-relative ./ paths in .env.example",
|
||||
),
|
||||
)
|
||||
|
||||
def test_runtime_paths_exposes_resolve_data_path(self) -> None:
|
||||
import util.runtime_paths as rp
|
||||
|
||||
self.assertTrue(callable(getattr(rp, "resolve_data_path", None)))
|
||||
self.assertTrue(callable(getattr(rp, "list_resolved_data_paths", None)))
|
||||
|
||||
def test_business_scripts_do_not_abspath_config_get(self) -> None:
|
||||
skill_root = get_skill_root()
|
||||
pattern = re.compile(r"os\.path\.abspath\s*\(\s*config\.get\b")
|
||||
offenders: list[str] = []
|
||||
for rel in _walk_files(skill_root, "scripts", suffix=".py"):
|
||||
if rel.replace("\\", "/") == "scripts/util/runtime_paths.py":
|
||||
continue
|
||||
text = _read_text(os.path.join(skill_root, rel))
|
||||
for lineno, line in enumerate(text.splitlines(), 1):
|
||||
if line.strip().startswith("#"):
|
||||
continue
|
||||
if pattern.search(line):
|
||||
offenders.append(f"{rel}:{lineno}: {line.strip()}")
|
||||
self.assertEqual(
|
||||
offenders,
|
||||
[],
|
||||
msg=_policy_msg(
|
||||
"POLICY-DATA-PATH-001",
|
||||
"development/DATA_PATHS.md",
|
||||
"\n".join(offenders),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TestPolicyDocs001(unittest.TestCase):
|
||||
def test_policy_matrix_exists_and_lists_policy_ids(self) -> None:
|
||||
skill_root = get_skill_root()
|
||||
|
||||
@@ -82,6 +82,68 @@ class TestRuntimePaths(unittest.TestCase):
|
||||
self.assertTrue(os.path.normpath(data_dir).startswith(os.path.normpath(tmp)))
|
||||
self.assertIn("_test", data_dir.replace("\\", "/"))
|
||||
|
||||
def test_resolve_data_path_defaults_under_skill_data_dir(self) -> None:
|
||||
with IsolatedDataRoot() as tmp:
|
||||
import importlib
|
||||
|
||||
import util.runtime_paths as rp
|
||||
|
||||
importlib.reload(rp)
|
||||
path = rp.resolve_data_path(None, "downloads", create=True)
|
||||
norm = os.path.normcase(os.path.normpath(path))
|
||||
norm_tmp = os.path.normcase(os.path.normpath(tmp))
|
||||
self.assertTrue(norm.startswith(norm_tmp))
|
||||
self.assertTrue(norm.endswith(os.path.normcase(os.path.join("downloads"))))
|
||||
|
||||
def test_resolve_data_path_relative_uses_data_dir_not_cwd(self) -> None:
|
||||
with IsolatedDataRoot() as tmp:
|
||||
import importlib
|
||||
|
||||
import util.runtime_paths as rp
|
||||
|
||||
importlib.reload(rp)
|
||||
os.environ["SKILL_DOWNLOAD_DIR"] = "downloads/videos"
|
||||
from jiangchang_skill_core import config
|
||||
|
||||
config.reset_cache()
|
||||
importlib.reload(rp)
|
||||
path = rp.resolve_data_path("SKILL_DOWNLOAD_DIR", "downloads", create=False)
|
||||
norm = os.path.normcase(os.path.normpath(path))
|
||||
self.assertIn(os.path.normcase("downloads"), norm)
|
||||
self.assertIn(os.path.normcase("videos"), norm)
|
||||
self.assertTrue(norm.startswith(os.path.normcase(os.path.normpath(tmp))))
|
||||
os.environ.pop("SKILL_DOWNLOAD_DIR", None)
|
||||
config.reset_cache()
|
||||
|
||||
def test_resolve_input_path_relative_under_imports(self) -> None:
|
||||
with IsolatedDataRoot() as tmp:
|
||||
import importlib
|
||||
|
||||
import util.runtime_paths as rp
|
||||
|
||||
importlib.reload(rp)
|
||||
resolved = rp.resolve_input_path("urls.txt", create_parent=True)
|
||||
norm = os.path.normcase(os.path.normpath(resolved))
|
||||
self.assertIn(os.path.normcase("imports"), norm)
|
||||
self.assertTrue(norm.startswith(os.path.normcase(os.path.normpath(tmp))))
|
||||
|
||||
def test_list_resolved_data_paths_keys(self) -> None:
|
||||
with IsolatedDataRoot():
|
||||
import importlib
|
||||
|
||||
import util.runtime_paths as rp
|
||||
|
||||
importlib.reload(rp)
|
||||
paths = rp.list_resolved_data_paths()
|
||||
for key in (
|
||||
"skill_data_dir",
|
||||
"downloads_dir",
|
||||
"imports_dir",
|
||||
"exports_dir",
|
||||
):
|
||||
self.assertIn(key, paths)
|
||||
self.assertTrue(paths[key])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user