Files
skill-template/tests/test_release_packaging_constraints.py
chendelian de127d7e71
All checks were successful
技能自动化发布 / release (push) Successful in 14s
docs: codify free PyArmor Trial packaging guidance
2026-10-13 15:55:44 +08:00

187 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""发布打包守护:PyArmor Trial 约定、单文件行数与 UTF-8 without BOM。"""
from __future__ import annotations
import os
import unittest
from _support import get_skill_root
SCRIPTS_MAX_LINES = 1000
SCRIPTS_RECOMMENDED_MAX_LINES = 900
TEXT_EXTENSIONS = frozenset(
{
".py",
".md",
".yml",
".yaml",
".json",
".toml",
".ini",
".txt",
".ps1",
".sh",
}
)
SPECIAL_TEXT_FILES = frozenset({".env.example", ".gitignore"})
SKIP_DIR_NAMES = frozenset(
{
".git",
"__pycache__",
".pytest_cache",
"rpa-artifacts",
"videos",
}
)
BINARY_EXTENSIONS = frozenset(
{
".db",
".sqlite",
".png",
".jpg",
".jpeg",
".gif",
".webp",
".mp4",
".zip",
".tar",
".gz",
}
)
def _is_text_candidate(path: str) -> bool:
basename = os.path.basename(path)
if basename in SPECIAL_TEXT_FILES:
return True
ext = os.path.splitext(basename)[1].lower()
if ext in BINARY_EXTENSIONS:
return False
return ext in TEXT_EXTENSIONS
def _iter_text_files(skill_root: str) -> list[str]:
found: list[str] = []
for root, dirnames, filenames in os.walk(skill_root):
dirnames[:] = [d for d in dirnames if d not in SKIP_DIR_NAMES]
for name in filenames:
full = os.path.join(root, name)
if _is_text_candidate(full):
found.append(full)
return sorted(found)
def _iter_scripts_py_files(skill_root: str) -> list[str]:
scripts_dir = os.path.join(skill_root, "scripts")
found: list[str] = []
if not os.path.isdir(scripts_dir):
return found
for root, dirnames, filenames in os.walk(scripts_dir):
dirnames[:] = [d for d in dirnames if d not in SKIP_DIR_NAMES]
for name in filenames:
if name.endswith(".py"):
found.append(os.path.join(root, name))
return sorted(found)
class TestReleasePackagingConstraints(unittest.TestCase):
def test_trial_packaging_guidance_is_documented(self) -> None:
skill_root = get_skill_root()
runtime_path = os.path.join(skill_root, "development", "RUNTIME.md")
development_path = os.path.join(skill_root, "development", "DEVELOPMENT.md")
with open(runtime_path, encoding="utf-8") as f:
runtime = f.read()
with open(development_path, encoding="utf-8") as f:
development = f.read()
required_guidance = {
"free/trial mode": (runtime + development, "Trial"),
"line limit is not sufficient": (runtime + development, "不是 Trial 加密成功的充分条件"),
"split the failing module by responsibility": (runtime + development, "按职责"),
"preserve compatibility imports": (runtime + development, "兼容门面"),
"verify real release encryption": (runtime + development, "实际加密"),
"inspect Gitea release result": (runtime + development, "Gitea"),
}
missing = [label for label, (text, needle) in required_guidance.items() if needle not in text]
self.assertEqual(missing, [], msg=f"Missing PyArmor Trial guidance: {missing}")
def test_scripts_py_files_under_pyarmor_line_limit(self) -> None:
skill_root = get_skill_root()
offenders: list[str] = []
for path in _iter_scripts_py_files(skill_root):
with open(path, encoding="utf-8") as f:
line_count = sum(1 for _ in f)
if line_count >= SCRIPTS_MAX_LINES:
rel = os.path.relpath(path, skill_root).replace("\\", "/")
offenders.append(
f"{rel}: {line_count} lines (limit < {SCRIPTS_MAX_LINES})"
)
self.assertEqual(
offenders,
[],
msg=(
"PyArmor trial/free mode has a preventive single-file line guard; "
"passing it does not guarantee Trial encryption succeeds. Split any "
"module identified by the actual Trial release error. "
f"Recommended keep each file < {SCRIPTS_RECOMMENDED_MAX_LINES} lines. "
f"Offenders: {offenders}"
),
)
def test_text_files_are_utf8_without_bom(self) -> None:
skill_root = get_skill_root()
bom_failures: list[str] = []
decode_failures: list[str] = []
feff_failures: list[str] = []
for path in _iter_text_files(skill_root):
rel = os.path.relpath(path, skill_root).replace("\\", "/")
with open(path, "rb") as f:
raw = f.read()
if raw.startswith(b"\xef\xbb\xbf"):
bom_failures.append(rel)
continue
try:
text = raw.decode("utf-8")
except UnicodeDecodeError as exc:
decode_failures.append(f"{rel}: {exc}")
continue
if "\ufeff" in text:
feff_failures.append(rel)
self.assertEqual(
bom_failures,
[],
msg=(
"Files must be saved as UTF-8 without BOM (remove UTF-8 BOM prefix). "
f"Offenders: {bom_failures}"
),
)
self.assertEqual(
decode_failures,
[],
msg=(
"Files must be valid UTF-8 without BOM. "
f"Offenders: {decode_failures}"
),
)
self.assertEqual(
feff_failures,
[],
msg=(
"Files must not contain U+FEFF; save as UTF-8 without BOM. "
f"Offenders: {feff_failures}"
),
)
if __name__ == "__main__":
unittest.main()