feat: 统一 svfs 子命令入口,打包进度回调与对象缓存路径

桌面应用(svfs-studio / svfs-client)要用的前置改动:

* 新增 src/sqlite_vfs/cli/main.py:`svfs <子命令>` 统一入口,旧的独立命令
  (vfspack / vfsunpack / …)保留为别名,两套跑同一段 execute(args);各子命令
  因此从 sys.exit() 改成返回退出码,便于被复用;
* FolderPacker.pack_folder(progress=…):每处理一个条目(目录也算)回调一次,
  供 GUI 进度条使用;只有传了回调时才做"只 stat、不读内容"的预扫描,CLI 的
  行为与开销不变;回调抛出的异常一路上抛——GUI 的中断语义就靠它;
* publish.default_cache_dir():对象缓存目录(客户端设置里可改);load_manifest
  对"误把服务器根地址当清单填进来"给出可用插件列表的提示,而不是只甩一个
  format=None。

测试:新增 tests/test_cli_main.py、tests/test_pack_progress.py,test_publish.py 补充。
This commit is contained in:
2026-09-16 17:15:16 +08:00
parent a7d7a8fb14
commit 06ec7c38bc
13 changed files with 836 additions and 86 deletions
+20
View File
@@ -449,6 +449,26 @@ class FetchOverHttpTest(HttpServerFixture):
with open(os.path.join(self.path("unverified"), "sub", "b.js"), "rb") as f:
self.assertEqual(b"console.log(2)", f.read())
def test_server_index_json_gets_actionable_error(self):
# 最常见的误操作:把服务器根地址(索引 JSON)当清单地址填进来,
# 报错要直接给出可用的插件 id,而不是一个看不懂的 format=None
index = self.path("index.json")
with open(index, "w", encoding="utf-8") as f:
json.dump({"service": "svfs-plugin-server",
"plugins": ["com.svfs.demo", "com.example.other"]}, f)
with self.assertRaises(ValueError) as ctx:
publish.load_manifest(index)
message = str(ctx.exception)
self.assertIn("服务器索引", message)
self.assertIn("com.svfs.demo", message)
empty = self.path("empty-index.json")
with open(empty, "w", encoding="utf-8") as f:
json.dump({"service": "svfs-plugin-server", "plugins": []}, f)
with self.assertRaises(ValueError) as ctx:
publish.load_manifest(empty)
self.assertIn("还没有插件", str(ctx.exception))
def test_manifest_from_url_mismatch_is_reported(self):
_src, objects, base, _db = self._export_and_serve()
with self.assertRaises(Exception):