Skip to content

Commit 1018a96

Browse files
authored
[fit] 新增build、package 命令 (#297)
* [fit] 新增build、package 命令 * [fit] 完善逻辑、增加类型校验 * [fit] 修改生成 tools.json 逻辑
1 parent 030b62e commit 1018a96

File tree

7 files changed

+493
-3
lines changed

7 files changed

+493
-3
lines changed
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import json
2+
import shutil
3+
import uuid
4+
import tarfile
5+
from datetime import datetime
6+
from pathlib import Path
7+
from fit_cli.utils.build import calculate_checksum, parse_python_file, type_errors
8+
9+
def generate_tools_json(base_dir: Path, plugin_name: str):
10+
"""生成 tools.json"""
11+
global type_errors
12+
type_errors.clear()
13+
14+
src_dir = base_dir / "src"
15+
if not src_dir.exists():
16+
print(f"❌ 未找到插件目录 {src_dir}")
17+
return None
18+
19+
build_dir = base_dir / "build"
20+
if not build_dir.exists():
21+
build_dir.mkdir(exist_ok=True)
22+
23+
tools_json = {
24+
"version": "1.0.0",
25+
"definitionGroups": [],
26+
"toolGroups": []
27+
}
28+
# 遍历src目录下的所有.py文件
29+
for py_file in src_dir.glob("**/*.py"):
30+
# 跳过__init__.py文件
31+
if py_file.name == "__init__.py":
32+
continue
33+
# 解析 Python 文件
34+
definition_group, tool_groups = parse_python_file(py_file)
35+
if definition_group is not None:
36+
tools_json["definitionGroups"].append(definition_group)
37+
if len(tool_groups) > 0:
38+
tools_json["toolGroups"].extend(tool_groups)
39+
40+
if type_errors:
41+
print("❌ tools.json 类型校验失败:")
42+
for err in set(type_errors):
43+
print(f" - {err}")
44+
print("请修改为支持的类型:int, float, str, bool, dict, list, tuple, set, bytes")
45+
return None # 终止构建
46+
47+
path = build_dir / "tools.json"
48+
path.write_text(json.dumps(tools_json, indent=2, ensure_ascii=False), encoding="utf-8")
49+
print(f"✅ 已生成 {path}")
50+
return tools_json
51+
52+
53+
def generate_plugin_json(base_dir: Path, plugin_name: str):
54+
"""生成 plugin.json"""
55+
build_dir = base_dir / "build"
56+
tar_path = build_dir / f"{plugin_name}.tar"
57+
if not tar_path.exists():
58+
print(f"❌ TAR 文件 {tar_path} 不存在,请先打包源代码")
59+
return None
60+
61+
checksum = calculate_checksum(tar_path)
62+
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
63+
short_uuid = str(uuid.uuid4())[:8]
64+
unique_name = f"{plugin_name}-{timestamp}-{short_uuid}"
65+
66+
plugin_json = {
67+
"checksum": checksum,
68+
"name": plugin_name,
69+
"description": f"{plugin_name} 插件",
70+
"type": "python",
71+
"uniqueness": {
72+
"name": unique_name
73+
}
74+
}
75+
path = build_dir / "plugin.json"
76+
path.write_text(json.dumps(plugin_json, indent=2, ensure_ascii=False), encoding="utf-8")
77+
print(f"✅ 已生成 {path}")
78+
return plugin_json
79+
80+
81+
def make_plugin_tar(base_dir: Path, plugin_name: str):
82+
"""打包源代码为 tar 格式"""
83+
build_dir = base_dir / "build"
84+
if not build_dir.exists():
85+
build_dir.mkdir(exist_ok=True)
86+
87+
tar_path = build_dir / f"{plugin_name}.tar"
88+
plugin_dir = base_dir
89+
90+
with tarfile.open(tar_path, "w") as tar:
91+
# 遍历插件目录下的所有文件
92+
for item in plugin_dir.rglob("*"):
93+
# 排除build目录及其内容
94+
if "build" in item.parts:
95+
continue
96+
97+
if item.is_file():
98+
arcname = Path(plugin_name) / item.relative_to(plugin_dir)
99+
tar.add(item, arcname=arcname)
100+
print(f"✅ 已打包源代码 {tar_path}")
101+
102+
103+
def run(args):
104+
"""build 命令入口"""
105+
base_dir = Path("plugin") / args.name
106+
plugin_name = args.name
107+
108+
if not base_dir.exists():
109+
print(f"❌ 插件目录 {base_dir} 不存在,请先运行 fit_cli init {args.name}")
110+
return
111+
# 生成 tools.json
112+
tools_json = generate_tools_json(base_dir, plugin_name)
113+
if tools_json is not None:
114+
make_plugin_tar(base_dir, plugin_name) # 打包源代码
115+
generate_plugin_json(base_dir, plugin_name) # 生成 plugin.json

framework/fit/python/fit_cli/commands/init_cmd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def hello(name: str) -> str: # 定义可供调用的函数,特别注意需要
1313
1414
修改函数名和参数
1515
- 函数名(hello)应根据功能调整,例如 concat, multiply
16-
- 参数(name: str)可以增加多个,类型也可以是 int, float 等
16+
- 参数(name: str)可以增加多个,类型支持 int, float, str, bool, dict, list, tuple, set, bytes, Union
1717
"""
1818
1919
return f"Hello, {name}!" # 提供函数实现逻辑
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import zipfile
2+
from pathlib import Path
3+
4+
def package_to_zip(plugin_name: str):
5+
"""将 build 生成的文件打包为 zip"""
6+
base_dir = Path("plugin") / plugin_name
7+
build_dir = base_dir / "build"
8+
9+
# 待打包的文件列表
10+
files_to_zip = [
11+
build_dir / f"{plugin_name}.tar",
12+
build_dir / "tools.json",
13+
build_dir / "plugin.json"
14+
]
15+
16+
# 检查文件是否存在
17+
missing_files = [str(f) for f in files_to_zip if not f.exists()]
18+
if missing_files:
19+
print(f"❌ 缺少以下文件,请先执行 build 命令:{', '.join(missing_files)}")
20+
return None
21+
22+
# 打包文件
23+
zip_path = build_dir / f"{plugin_name}_package.zip"
24+
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
25+
for file in files_to_zip:
26+
zipf.write(file, arcname=file.name)
27+
28+
print(f"✅ 已生成打包文件:{zip_path}")
29+
return zip_path
30+
31+
32+
def run(args):
33+
"""package 命令入口"""
34+
plugin_name = args.name
35+
base_dir = Path("plugin") / plugin_name
36+
build_dir = base_dir / "build"
37+
38+
if not base_dir.exists():
39+
print(f"❌ 插件目录 {base_dir} 不存在,请先运行 init 和 build 命令")
40+
return
41+
if not build_dir.exists():
42+
print(f"❌ 构建目录 {build_dir} 不存在,请先运行 build 命令")
43+
return
44+
45+
package_to_zip(plugin_name)

framework/fit/python/fit_cli/main.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import argparse
22
from fit_cli.commands import init_cmd
3+
from fit_cli.commands import build_cmd
4+
from fit_cli.commands import package_cmd
35

46
def main():
57
parser = argparse.ArgumentParser(description="FIT Framework CLI 插件开发工具")
@@ -10,6 +12,16 @@ def main():
1012
parser_init.add_argument("name", help="插件目录名称")
1113
parser_init.set_defaults(func=init_cmd.run)
1214

15+
# build
16+
parser_build = subparsers.add_parser("build", help="构建插件,生成 tools.json / plugin.json")
17+
parser_build.add_argument("name", help="插件目录名称")
18+
parser_build.set_defaults(func=build_cmd.run)
19+
20+
# package
21+
parser_package = subparsers.add_parser("package", help="将插件文件打包")
22+
parser_package.add_argument("name", help="插件目录名称")
23+
parser_package.set_defaults(func=package_cmd.run)
24+
1325
args = parser.parse_args()
1426

1527
if hasattr(args, "func"):
Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,61 @@
11
# FIT CLI 工具
22

3-
FIT CLI 工具是基于 **FIT Framework** 的命令行开发工具,提供插件初始化、打包、发布等功能,帮助用户快速开发和管理 FIT 插件。
3+
FIT CLI 工具是基于 **FIT Framework** 的命令行开发工具,提供插件初始化、构建、打包等功能,帮助用户快速开发和管理 FIT 插件。
44

55
---
66

77
## 使用方式
88

9+
FIT CLI 支持 3 个核心子命令:init(初始化)、build(构建)、package(打包),以下是详细说明。
10+
11+
### init
12+
913
以 framework/fit/python 为项目根目录,运行:
1014

1115
```bash
1216
python -m fit_cli init %{your_plugin_name}
1317
```
14-
将会在 plugin 目录中创建 %{your_plugin_name} 目录,并生成插件模板。
18+
· 参数:``%{your_plugin_name}``: 自定义插件名称
19+
20+
会在 ``plugin`` 目录中创建如下结构:
21+
22+
└── plugin/
23+
└──%{your_plugin_name}/
24+
└── src/
25+
├── __init__.py
26+
└── plugin.py # 插件源码模板
27+
28+
### build
29+
30+
在完成插件的开发后,执行
31+
```bash
32+
python -m fit_cli build %{your_plugin_name}
33+
```
34+
· 参数:``%{your_plugin_name}``: 插件目录名称
35+
36+
``%{your_plugin_name}`` 目录生成:
37+
38+
└──%{your_plugin_name}/
39+
└── build/
40+
├── %{your_plugin_name}.tar # 插件源码打包文件(工具包)。
41+
├── tools.json # 工具的元数据。
42+
└── plugin.json # 插件的完整性校验与唯一性校验以及插件的基本信息。
43+
44+
开发者可根据自己的需要,修改完善``tools.json````plugin.json`` 文件,比如修改 ``description````uniqueness``等条目。
45+
46+
### package
47+
48+
在完成插件的构建后,执行
49+
```bash
50+
python -m fit_cli package %{your_plugin_name}
51+
```
52+
· 参数:``%{your_plugin_name}``: 插件目录名称
53+
54+
``plugin/%{your_plugin_name}/build/`` 目录生成最终打包文件: ``%{your_plugin_name}_package.zip``
55+
56+
---
57+
58+
## 注意事项
59+
60+
1. 运行命令前,请切换至 framework/fit/python 项目根目录。
61+
2. 更多详细信息和使用说明,可参考 https://github.com/ModelEngine-Group/fit-framework 官方仓库。

framework/fit/python/fit_cli/utils/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)