Add comprehensive tests for image processing nodes and refactor node imports

- Introduced new test files for image layer, image plane, normal scale, object normal rotation, shadeless, and temporary attributes.
- Implemented tests to validate the behavior of alpha curves, depth layers, and normal scales in various rendering engines.
- Refactored import paths for node groups to align with the new directory structure.
- Added checks for temporary attribute lifetimes and ensured proper cleanup without warnings.
- Enhanced CLI commands to require the Blender group and validate the presence of generated node assets before packaging.
- Updated packaging scripts to enforce the existence of necessary assets, preventing build failures.
This commit is contained in:
2026-09-19 21:23:19 +08:00
parent 2b3c0194a6
commit 9debab5c0c
73 changed files with 500 additions and 217 deletions

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-09-19

View File

@@ -0,0 +1,112 @@
## Context
节点组的可维护源码位于 `tools/nodes/groups` 和 `tools/nodes/common`,构建入口、布局、校验与预览也位于同一包中。生成后的 `src/anyimage/assets/O_AnyImage.blend` 被扩展运行时读取并进入发布包,但它同时作为 Git LFS 文件提交,历史中已累积多个 1–10 MB 的版本。
项目通过 `blender` dependency group 安装 `bpy==4.5.3`,现有测试也使用该 wheel 创建节点、求值几何及写入 `.blend`。因此本地和 CI 都不需要额外安装或定位完整 Blender 应用程序。
## Goals / Non-Goals
**Goals:**
- 根目录 `nodes/` 只表达节点组及共用节点构建定义。
- `tools/nodes/` 只表达构建、布局、校验和预览工具。
- 干净 CI 从源码生成、验证并打包节点资产。
- 本地与 CI 使用同一个 `node-group` 执行路径,不需要 `.env` 或 `BLENDER_BIN`。
- Git 当前树不再包含生成的 `.blend` 或对应 LFS pointer。
- 节点组的接口、默认值、求值和最终发布包保持不变。
**Non-Goals:**
- 不在本变更中重写 Git 历史或清理远程 LFS 对象。
- 不改变节点组业务名称和实现逻辑。
- 不把节点资产改为扩展运行时动态创建。
- 不提供选择其他 Blender 可执行程序或版本的兼容路径。
## Decisions
### 节点定义成为独立顶层包
建立 `nodes/` Python package:
```text
nodes/
├── common/
└── groups/
tools/nodes/
├── arrangement/
├── preview/
├── build.py
├── check.py
└── cli.py
```
`nodes/groups` 保存各命名节点组定义,`nodes/common` 保存这些定义共享的节点图构建逻辑。布局属于产物呈现和构建工具,继续位于 `tools/nodes/arrangement`。旧 `tools.nodes.groups` 与 `tools.nodes.common` 路径直接删除,不提供转发模块。
Hatch 的工具 wheel同时包含 `tools` 和 `nodes`,确保安装后的 `node-group` 入口能导入节点定义。相应测试按同一职责拆分:节点行为测试进入 `tests/nodes`,工具入口、布局、保存资产和校验测试保留在 `tests/tools/nodes`。
### `.blend` 是构建产物而不是源码
`src/anyimage/assets/O_AnyImage.blend` 加入 `.gitignore` 并从 Git 索引移除。源码检出不承诺该文件存在;开发者使用 `node-group build` 生成它。扩展运行时仍只通过 `load_node_group` 加载生成资产,不在用户运行时创建节点组。
保留 `.gitattributes` 的通用 Blender LFS 规则,因为它可能服务其他手工资产;忽略规则确保本生成物不再加入 Git。删除当前文件只影响未来提交,历史容量将在后续仓库历史整理时处理。
### node-group 统一使用 bpy wheel
`node-group build`、`check` 和 `preview` 都在 `uv run --group blender` 提供的 Python 环境中工作。CLI 不再解析 `--blender`,也不再读取 `BLENDER_BIN`、项目 `.env` 或系统 `PATH`。
为保持构建与保存后检查的进程隔离,CLI 使用 `sys.executable -m` 顺序启动模块:
```text
node-group build
├── python -m tools.nodes.build
├── python -m tools.nodes.check
└── python -m pytest tests/nodes tests/tools/nodes
```
`check` 只运行后两步,`preview` 使用同一 Python 运行预览导出模块。节点命令只运行与节点定义和工具直接相关的测试;项目常规与 AI 测试由各自命令负责。缺少 `bpy` 时命令明确提示使用 `uv run --group blender node-group ...`,不回退到外部 Blender。
相比保留完整 Blender 启动器,这一方案删除了路径发现、`.env` 解析、wheel 解压和 `--python-expr` bootstrap,且让本地与 CI 覆盖同一入口。
### CI 通过 node-group 构建
测试工作流在 pytest 前执行:
```bash
uv run --group blender node-group build --skip-tests
```
该命令使用 CI 已安装的 `bpy==4.5.3`,顺序生成并检查资产。随后运行常规测试目录,节点资产测试读取刚生成的文件。
发布工作流采用同样顺序,在测试成功后执行 `uv run pack`。生成文件留在 job 工作区,由打包器收入扩展压缩包,不作为工作流 artifact 单独传递。
备选方案是在本地或 CI 查找完整 Blender。它下载更大、存在第二套 Python 和依赖 bootstrap,且现有 bpy wheel 已覆盖所需 API,因此删除而不是保留兼容路径。
### 缺失资产时打包明确失败
打包器在创建任何平台包前检查生成资产存在且为普通文件;缺失时给出先构建节点资产的错误。打包测试既验证缺失失败,也验证每个平台归档包含 `assets/O_AnyImage.blend`。
这避免生成步骤遗漏后发布一个运行时才报错的不完整扩展。
## Risks / Trade-offs
- [bpy wheel 的资产兼容性发生变化] → 固定 `bpy==4.5.3`,构建后在独立 Python 进程运行资产检查和现有求值测试。
- [开发者从源码直接启用扩展时缺少资产] → `node-group build` 成为源码使用和打包前置步骤,缺失资产由工具给出明确错误。
- [测试路径重组造成覆盖遗漏] → 沿真实导入路径迁移测试并运行节点构建、常规测试和全量测试。
- [生成文件被误提交] → `.gitignore` 固定忽略具体资产路径,并增加版本控制边界测试。
- [从 Git 删除文件不会立即释放现有远程 LFS 容量] → 本变更只阻止继续增长;后续创建精简根历史时排除旧 LFS 对象。
## Migration Plan
1. 移动节点定义及对应测试,更新全部导入与 wheel 包清单。
2. 将 `node-group` 改为通过当前 Python 的独立子进程运行构建、检查和预览,删除完整 Blender 定位与 bootstrap。
3. 在保留现有资产的情况下构建并验证,确认重组没有改变节点行为。
4. 为测试和发布工作流加入统一的 `node-group` 生成步骤,补充缺失资产打包测试。
5. 将生成资产加入 `.gitignore` 并从 Git 索引移除。
6. 从无节点资产的干净状态执行 CI 等价流程,确认最终发布包包含重新生成的资产。
回滚时恢复节点定义路径、工作流和已提交资产即可;Git 历史清理不属于本变更,因此回滚不需要恢复远程历史。
## Open Questions
无。

View File

@@ -0,0 +1,35 @@
## Why
`O_AnyImage.blend` 可以由 Python 节点定义完整重建,却长期作为 Git LFS 资产提交,每次更新都会累积新的二进制历史。与此同时,节点组业务定义与构建、校验、预览工具都位于 `tools/nodes`,源码边界没有体现“节点组”和“节点构建工具”的区别。
## What Changes
- 将 `tools/nodes/groups` 和 `tools/nodes/common` 移至根目录 `nodes/groups` 和 `nodes/common`,作为节点组及其共用构建定义。
- `tools/nodes` 仅保留构建入口、资产校验、布局和预览等开发工具。
- 更新源码、测试、打包配置和工具入口中的导入路径,不保留旧路径兼容层。
- `node-group` 统一使用 `blender` dependency group 提供的 `bpy` wheel,不再查找或启动本地 Blender 应用程序。
- 删除 `--blender` 参数、`BLENDER_BIN` 环境变量、项目 `.env` 读取及其相关测试和说明。
- **BREAKING**:`src/anyimage/assets/O_AnyImage.blend` 不再纳入 Git/LFS;干净源码检出必须先构建节点资产,才能直接加载扩展或打包。
- 测试 CI 和发布 CI 使用 `bpy` wheel 在运行测试或打包前生成并校验节点资产。
- 打包流程继续要求并包含生成后的 `O_AnyImage.blend`,缺少资产时明确失败。
- 更新节点资产内部文档和项目规范,使源码、构建产物及 CI 职责一致。
## Capabilities
### New Capabilities
- `generated-node-assets`: 定义节点资产从源码生成、在 CI 中验证并进入发布包的完整契约。
### Modified Capabilities
- `maintainable-project-structure`: 将节点组业务定义与节点资产开发工具拆分为 `nodes/` 和 `tools/nodes/` 两个职责边界。
- `developer-tool-cli`: 节点工具改为通过当前 uv Python 的 `bpy` wheel 构建、校验和预览,不再依赖外部 Blender 可执行程序。
## Impact
- 移动 `tools/nodes/groups`、`tools/nodes/common` 及其全部导入引用,并将根目录 `nodes` 纳入开发工具 wheel。
- 修改 `.github/workflows/test.yml`、`.github/workflows/release.yml`、节点工具测试和打包测试。
- 简化 `tools/nodes/cli.py`,删除 Blender 路径发现、`.env` 配置和外部 Blender bootstrap。
- 从版本控制移除 `src/anyimage/assets/O_AnyImage.blend`,更新 `.gitignore` 和 LFS 跟踪状态。
- 修改 `AGENTS.md` 与 `docs/internals/node-assets.md` 中关于节点资产提交和构建的规则。
- 不改变节点组名称、接口、求值行为或最终扩展包内容。

View File

@@ -0,0 +1,41 @@
## MODIFIED Requirements
### Requirement: Build and check node groups
`node-group build` SHALL 使用当前 Python 环境中的 `bpy`,通过独立 Python 子进程依次构建资产、校验保存的资产、运行 `tests/nodes` 与 `tests/tools/nodes`;`node-group check` SHALL 仅执行后两步。两者 SHALL 支持 `--skip-tests` 仅跳过节点相关 pytest,且 SHALL 不查找或启动外部 Blender 可执行程序。
#### Scenario: Build assets
- **WHEN** 用户运行 `uv run --group blender node-group build`
- **THEN** 使用固定 bpy 环境更新 O_AnyImage.blend,并在独立进程的资产校验和节点相关测试成功后成功退出
#### Scenario: Check existing assets
- **WHEN** 用户运行 `uv run --group blender node-group check`
- **THEN** 使用固定 bpy 环境检查已有资产并运行节点相关测试,资产文件内容保持原样
#### Scenario: Skip Python tests
- **WHEN** build 或 check 指定 `--skip-tests`
- **THEN** 仍执行独立进程的资产校验,跳过 pytest
#### Scenario: Stop after failure
- **WHEN** 构建、校验或测试任一步失败
- **THEN** 命令非零退出且不执行后续步骤
#### Scenario: Missing bpy environment
- **WHEN** 当前 Python 环境无法导入 bpy
- **THEN** 命令非零退出并提示使用 `--group blender`,不尝试查找外部 Blender
### Requirement: Preview node groups
`node-group preview` SHALL 使用当前 Python 环境中的 `bpy`,从当前源码在独立 Python 子进程中构建并排列几何节点组,生成 HTML,输出绝对路径并默认打开浏览器,保持资产文件内容原样。默认文件 SHALL 保存于系统临时目录并在命令退出后可供查看。命令 SHALL 支持 `--output PATH`、`--no-open` 和 `--fragment`,不支持选择外部 Blender 可执行程序。
#### Scenario: Preview current source
- **WHEN** 用户运行 `uv run --group blender node-group preview`
- **THEN** 生成当前源码的可查看 HTML 并尝试打开浏览器
#### Scenario: Save without opening
- **WHEN** 用户指定 `--output PATH --no-open`
- **THEN** HTML 写入指定路径且不启动浏览器
#### Scenario: Export fragment
- **WHEN** 用户指定 `--fragment`
- **THEN** 输出 HTML 片段及其路径,不自动打开浏览器

View File

@@ -0,0 +1,37 @@
## ADDED Requirements
### Requirement: Node asset is generated from source
系统 SHALL 能够仅根据版本控制中的 `nodes/` 定义和 `tools/nodes` 构建工具生成 `src/anyimage/assets/O_AnyImage.blend`。生成文件 SHALL 不纳入 Git 版本控制。
#### Scenario: Build from a clean checkout
- **WHEN** 干净源码检出中不存在 `O_AnyImage.blend` 并执行节点资产构建
- **THEN** 构建成功生成包含全部既定节点组的资产文件
#### Scenario: Rebuild an existing asset
- **WHEN** 输出路径已有本地生成的节点资产并再次执行构建
- **THEN** 文件被当前源码生成的完整资产替换,不产生 `.blend1` 备份
### Requirement: CI validates generated node assets
测试 CI SHALL 通过统一的 `node-group` 入口和固定版本的 `bpy` wheel,在运行节点资产相关测试前生成并独立校验节点资产,不得要求额外的 Blender 应用程序安装。
#### Scenario: Validate a pull request
- **WHEN** 测试工作流检出不含节点资产的源码
- **THEN** 工作流生成资产、校验保存结果并运行读取该资产的测试
#### Scenario: Reject an invalid generated asset
- **WHEN** 节点源码生成的资产未通过接口、结构或基本求值检查
- **THEN** CI 在运行后续发布步骤前失败
### Requirement: Release packages contain the generated asset
发布 CI SHALL 在测试和打包前生成并验证节点资产。每个平台扩展归档 MUST 包含 `assets/O_AnyImage.blend`,打包器 MUST 在资产缺失时明确失败。
#### Scenario: Package a release
- **WHEN** 发布工作流从干净源码构建扩展
- **THEN** 每个平台归档包含本次工作流生成并验证的节点资产
#### Scenario: Package without building assets
- **WHEN** 打包器运行时 `O_AnyImage.blend` 不存在
- **THEN** 打包立即失败并提示先构建节点资产

View File

@@ -0,0 +1,21 @@
## MODIFIED Requirements
### Requirement: Node asset modules reflect responsibilities
根目录 `nodes/` 的模块 MUST 只包含节点组及其共用构建定义,`tools/nodes/` MUST 只包含构建入口、布局、资产校验和预览工具。所有移动 MUST 同步导入、wheel 包清单、构建与验证入口及测试路径,并删除旧路径和转发层。
#### Scenario: Load reorganized node definitions
- **WHEN** 构建工具和节点行为测试导入节点组定义
- **THEN** 导入指向 `nodes.groups` 或 `nodes.common`,不存在 `tools.nodes.groups` 或 `tools.nodes.common` 兼容路径
#### Scenario: Load node asset tools
- **WHEN** 启动器加载节点构建、布局、验证或预览模块
- **THEN** 工具从 `tools.nodes` 加载,并从顶层 `nodes` 包使用节点定义
#### Scenario: Preserve node behavior
- **WHEN** 对重组后的节点构建函数运行针对性测试
- **THEN** 节点组名称、接口、默认值、运算、连接、布局和求值与重组前一致
#### Scenario: Locate node tests
- **WHEN** 维护者按测试路径查找节点相关覆盖
- **THEN** 节点定义行为测试位于 `tests/nodes`,构建与资产工具测试位于 `tests/tools/nodes`

View File

@@ -0,0 +1,36 @@
## 1. 拆分节点源码与工具
- [x] 1.1 创建顶层 `nodes` package,将 `tools/nodes/groups` 和 `tools/nodes/common` 移入并删除旧路径
- [x] 1.2 更新构建、布局、预览、测试和辅助代码的导入,使节点定义只从 `nodes.groups` 与 `nodes.common` 加载
- [x] 1.3 将 `nodes` 加入 Hatch wheel 包清单,验证安装后的 `node-group` 入口可加载节点定义
- [x] 1.4 将节点定义行为测试移动到 `tests/nodes`,仅将构建、布局、校验和预览工具测试保留在 `tests/tools/nodes`
## 2. 将节点资产改为构建产物
- [x] 2.1 将 `src/anyimage/assets/O_AnyImage.blend` 加入 `.gitignore` 并从 Git 索引移除,不改变扩展运行时资产路径
- [x] 2.2 为打包器增加节点资产存在性检查,并覆盖缺失失败与归档包含资产的行为
- [x] 2.3 增加版本控制边界测试,确认生成的节点资产不会再次进入 Git
## 3. 统一节点工具运行环境
- [x] 3.1 删除 `--blender`、`BLENDER_BIN`、`.env` 读取、Blender 路径发现及外部进程 bootstrap
- [x] 3.2 让 build、check 和 preview 通过当前 Python 的独立子进程运行,让 build/check 只运行节点相关测试,并在缺少 bpy 时给出 `--group blender` 提示
- [x] 3.3 更新节点 CLI 测试,覆盖命令顺序、参数传递、失败停止和不再支持外部 Blender 配置
## 4. 接入 CI 构建
- [x] 4.1 修改测试工作流,通过统一的 `node-group` 入口从干净源码生成并校验节点资产后再运行常规测试
- [x] 4.2 修改发布工作流,在测试和打包前生成并校验节点资产,确保生成文件进入全部平台归档
- [x] 4.3 更新工作流测试,验证 CI 不要求 `BLENDER_BIN` 或额外下载完整 Blender
## 5. 同步规范与文档
- [x] 5.1 更新 `AGENTS.md`,将节点资产定义为必须由源码构建、不得提交的发布构建产物,并删除外部 Blender 配置规则
- [x] 5.2 更新 `docs/internals/node-assets.md` 及相关内部引用,说明 `nodes/`、`tools/nodes/`、bpy 环境和 CI 生成链
## 6. 验证
- [x] 6.1 从不存在 `O_AnyImage.blend` 的状态运行统一的节点资产构建与独立检查
- [x] 6.2 运行节点定义、节点工具、常规及全量测试,确认重组前后节点行为一致
- [x] 6.3 运行打包相关测试并检查生成归档,确认包含节点资产且源码树未重新跟踪该文件
- [x] 6.4 搜索旧导入、外部 Blender 配置和路径引用,检查最终差异并严格验证 OpenSpec 变更