feat: add GitHub Actions workflow for running tests on push and pull request
Some checks failed
运行测试 / test (push) Failing after 16m31s
构建并部署文档 / deploy (push) Failing after 8m38s

This commit is contained in:
2026-08-19 21:38:08 +08:00
parent 9bb603b532
commit ee46d98b93
9 changed files with 755 additions and 260 deletions

101
pack.py
View File

@@ -1,4 +1,8 @@
import subprocess
import sys
import tempfile
import tomllib
from dataclasses import dataclass
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
@@ -6,11 +10,57 @@ from zipfile import ZIP_DEFLATED, ZipFile
PROJECT_ROOT = Path(__file__).resolve().parent
SOURCE_DIR = PROJECT_ROOT / "src" / "snapform"
DIST_DIR = PROJECT_ROOT / "dist"
PLATFORMS = ("windows-x64", "macos-arm64", "linux-x64")
PYTHON_VERSION = "3.11"
def manifest_for_platform(platform):
"""Return the manifest text declaring only the given platform."""
@dataclass(frozen=True)
class Platform:
pip_platform: str
PLATFORM_CONFIGS = {
"windows-x64": Platform(pip_platform="win_amd64"),
"macos-arm64": Platform(pip_platform="macosx_12_0_arm64"),
"linux-x64": Platform(pip_platform="manylinux2014_x86_64"),
}
PLATFORMS = tuple(PLATFORM_CONFIGS)
def project_dependencies():
"""Return Extension wheel requirements declared by the project."""
project = tomllib.loads(
(PROJECT_ROOT / "pyproject.toml").read_text(encoding="utf-8")
)["project"]
return project.get("dependencies", [])
def download_wheels(platform, destination):
"""Download default dependencies as wheels for one target platform."""
dependencies = project_dependencies()
if not dependencies:
return []
config = PLATFORM_CONFIGS[platform]
command = [
sys.executable,
"-m",
"pip",
"download",
*dependencies,
"--dest",
str(destination),
"--only-binary=:all:",
f"--python-version={PYTHON_VERSION}",
f"--platform={config.pip_platform}",
]
subprocess.run(command, check=True)
wheels = sorted(destination.glob("*.whl"))
if not wheels:
raise RuntimeError(f"No wheels downloaded for {platform}")
return wheels
def manifest_for_platform(platform, wheels):
"""Return manifest text for one platform and its downloaded wheels."""
text = (SOURCE_DIR / "blender_manifest.toml").read_text(encoding="utf-8")
lines = text.splitlines()
table_start = next(
@@ -19,22 +69,30 @@ def manifest_for_platform(platform):
)
head = lines[:table_start]
tail = lines[table_start:]
head = [line for line in head if not line.strip().startswith("platforms =")]
# 插入顶层区域,避免 platforms 被解析为后续 table 的子键。
# 放在 blender_version_min 后面,使扩展元数据保持易读。
head = [
line
for line in head
if not line.strip().startswith(("platforms =", "wheels ="))
]
insert_at = next(
(i + 1 for i, line in enumerate(head) if line.strip().startswith("blender_version_min")),
(
i + 1
for i, line in enumerate(head)
if line.strip().startswith("blender_version_min")
),
len(head),
)
head.insert(insert_at, f'platforms = ["{platform}"]')
lines = head + tail
return "\n".join(lines) + "\n"
wheel_names = ", ".join(
f'"./wheels/{wheel.name}"' for wheel in wheels
)
head.insert(insert_at + 1, f"wheels = [{wheel_names}]")
return "\n".join(head + tail) + "\n"
def build_extension(platform):
if platform not in PLATFORMS:
raise ValueError(f"Unsupported platform: {platform}")
manifest = tomllib.loads(manifest_for_platform(platform))
def _build_extension(platform, wheels):
manifest_text = manifest_for_platform(platform, wheels)
manifest = tomllib.loads(manifest_text)
output_path = DIST_DIR / (
f"{manifest['name']}.v{manifest['version']}.{platform}.zip"
)
@@ -46,16 +104,25 @@ def build_extension(platform):
if "__pycache__" in source_path.parts:
continue
relative_path = source_path.relative_to(SOURCE_DIR)
if relative_path.parts[0] == "wheels":
continue
if source_path.name == "blender_manifest.toml":
continue
archive.write(source_path, relative_path)
archive.writestr(
"blender_manifest.toml",
manifest_for_platform(platform),
)
archive.writestr("blender_manifest.toml", manifest_text)
for wheel in wheels:
archive.write(wheel, Path("wheels") / wheel.name)
return output_path
def build_extension(platform):
if platform not in PLATFORMS:
raise ValueError(f"Unsupported platform: {platform}")
with tempfile.TemporaryDirectory() as directory:
wheels = download_wheels(platform, Path(directory))
return _build_extension(platform, wheels)
def build_all():
return [build_extension(platform) for platform in PLATFORMS]