nova-forge 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nova_forge-0.1.0/LICENSE +21 -0
- nova_forge-0.1.0/PKG-INFO +79 -0
- nova_forge-0.1.0/README.md +59 -0
- nova_forge-0.1.0/pyproject.toml +36 -0
- nova_forge-0.1.0/setup.cfg +4 -0
- nova_forge-0.1.0/src/nova_forge/__init__.py +1 -0
- nova_forge-0.1.0/src/nova_forge/assets/__init__.py +0 -0
- nova_forge-0.1.0/src/nova_forge/cli.py +30 -0
- nova_forge-0.1.0/src/nova_forge/core/__init__.py +0 -0
- nova_forge-0.1.0/src/nova_forge/core/scaffolder.py +91 -0
- nova_forge-0.1.0/src/nova_forge/core/templates.py +332 -0
- nova_forge-0.1.0/src/nova_forge/core/tree_builder.py +248 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/PKG-INFO +79 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/SOURCES.txt +16 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/dependency_links.txt +1 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/entry_points.txt +2 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/requires.txt +2 -0
- nova_forge-0.1.0/src/nova_forge.egg-info/top_level.txt +1 -0
nova_forge-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 单保录 (eagleye-china)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nova-forge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds.
|
|
5
|
+
Author-email: eagleye-china <zdt168@sina.cn>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/eagleye-china/Nova-Forge
|
|
8
|
+
Project-URL: Repository, https://github.com/eagleye-china/Nova-Forge
|
|
9
|
+
Project-URL: Issues, https://github.com/eagleye-china/Nova-Forge/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: build>=1.0.0
|
|
18
|
+
Requires-Dist: twine>=5.0.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Nova-Forge 🛠️
|
|
22
|
+
|
|
23
|
+
> **The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds.**
|
|
24
|
+
|
|
25
|
+
**English** | [简体中文](https://github.com/eagleye-china/Nova-Forge/blob/main/README_zh.md)
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/nova-forge/)
|
|
28
|
+
[](https://pypi.org/project/nova-forge/)
|
|
29
|
+
[](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE)
|
|
30
|
+
[](https://github.com/eagleye-china/Nova-Forge)
|
|
31
|
+
|
|
32
|
+
**Nova-Forge** is a blazing-fast CLI scaffolding tool designed to eliminate the boilerplate of modern Python desktop applications.
|
|
33
|
+
|
|
34
|
+
Stop copying and pasting `pyproject.toml`, fumbling with MIT licenses, or manually creating hundreds of folders. With a single command, Nova-Forge generates a complete, production-ready `src-layout` project equipped with CI/CD, bilingual docs, and PyPI packaging scripts.
|
|
35
|
+
|
|
36
|
+
## ✨ Features
|
|
37
|
+
|
|
38
|
+
- 🚀 **`nova-forge init`**: Generates `pyproject.toml`, bilingual `README.md`, `LICENSE`, and a GitHub Actions workflow for automatic PyPI publishing.
|
|
39
|
+
- 🌲 **`nova-forge tree`**: Builds an entire directory structure instantly from a simple text-based tree diagram.
|
|
40
|
+
- 🤖 **AI-Ready**: Perfect for pairing with AI coding assistants (like DeepSeek or ChatGPT) to instantly materialize AI-generated architectures.
|
|
41
|
+
|
|
42
|
+
## 📦 Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install nova-forge
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 🛠️ Usage
|
|
49
|
+
|
|
50
|
+
### 1. Initialize Project Configuration
|
|
51
|
+
Run this in your empty project folder. It will interactively ask for your project details and generate all necessary metadata.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
nova-forge init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Build Directory Structure
|
|
58
|
+
Got a project layout from your AI architect? Paste it into `project_structure.txt`:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
my-awesome-app/
|
|
62
|
+
├── src/
|
|
63
|
+
│ └── my_awesome_app/
|
|
64
|
+
│ ├── __init__.py
|
|
65
|
+
│ └── main.py
|
|
66
|
+
└── examples/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Then run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
nova-forge tree
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
*Boom! Your entire folder structure is created with auto-generated `__init__.py` and boilerplate code.*
|
|
76
|
+
|
|
77
|
+
## 📜 License
|
|
78
|
+
|
|
79
|
+
MIT License. See [LICENSE](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Nova-Forge 🛠️
|
|
2
|
+
|
|
3
|
+
> **The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds.**
|
|
4
|
+
|
|
5
|
+
**English** | [简体中文](https://github.com/eagleye-china/Nova-Forge/blob/main/README_zh.md)
|
|
6
|
+
|
|
7
|
+
[](https://pypi.org/project/nova-forge/)
|
|
8
|
+
[](https://pypi.org/project/nova-forge/)
|
|
9
|
+
[](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE)
|
|
10
|
+
[](https://github.com/eagleye-china/Nova-Forge)
|
|
11
|
+
|
|
12
|
+
**Nova-Forge** is a blazing-fast CLI scaffolding tool designed to eliminate the boilerplate of modern Python desktop applications.
|
|
13
|
+
|
|
14
|
+
Stop copying and pasting `pyproject.toml`, fumbling with MIT licenses, or manually creating hundreds of folders. With a single command, Nova-Forge generates a complete, production-ready `src-layout` project equipped with CI/CD, bilingual docs, and PyPI packaging scripts.
|
|
15
|
+
|
|
16
|
+
## ✨ Features
|
|
17
|
+
|
|
18
|
+
- 🚀 **`nova-forge init`**: Generates `pyproject.toml`, bilingual `README.md`, `LICENSE`, and a GitHub Actions workflow for automatic PyPI publishing.
|
|
19
|
+
- 🌲 **`nova-forge tree`**: Builds an entire directory structure instantly from a simple text-based tree diagram.
|
|
20
|
+
- 🤖 **AI-Ready**: Perfect for pairing with AI coding assistants (like DeepSeek or ChatGPT) to instantly materialize AI-generated architectures.
|
|
21
|
+
|
|
22
|
+
## 📦 Installation
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install nova-forge
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 🛠️ Usage
|
|
29
|
+
|
|
30
|
+
### 1. Initialize Project Configuration
|
|
31
|
+
Run this in your empty project folder. It will interactively ask for your project details and generate all necessary metadata.
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
nova-forge init
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Build Directory Structure
|
|
38
|
+
Got a project layout from your AI architect? Paste it into `project_structure.txt`:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
my-awesome-app/
|
|
42
|
+
├── src/
|
|
43
|
+
│ └── my_awesome_app/
|
|
44
|
+
│ ├── __init__.py
|
|
45
|
+
│ └── main.py
|
|
46
|
+
└── examples/
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Then run:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
nova-forge tree
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
*Boom! Your entire folder structure is created with auto-generated `__init__.py` and boilerplate code.*
|
|
56
|
+
|
|
57
|
+
## 📜 License
|
|
58
|
+
|
|
59
|
+
MIT License. See [LICENSE](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.2", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "nova-forge"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "eagleye-china", email = "zdt168@sina.cn" }]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3.10",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Topic :: Software Development :: Code Generators",
|
|
18
|
+
]
|
|
19
|
+
# ✅ 补全依赖,因为生成的脚本需要 build 和 twine 来发版
|
|
20
|
+
dependencies = [
|
|
21
|
+
"build>=1.0.0",
|
|
22
|
+
"twine>=5.0.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/eagleye-china/Nova-Forge"
|
|
27
|
+
Repository = "https://github.com/eagleye-china/Nova-Forge"
|
|
28
|
+
Issues = "https://github.com/eagleye-china/Nova-Forge/issues"
|
|
29
|
+
|
|
30
|
+
[tool.setuptools.packages.find]
|
|
31
|
+
where = ["src"]
|
|
32
|
+
include = ["nova_forge*"]
|
|
33
|
+
|
|
34
|
+
# ✅ 核心:注册全局命令
|
|
35
|
+
[project.scripts]
|
|
36
|
+
nova-forge = "nova_forge.cli:main"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.0"
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from .core.scaffolder import run_scaffolder
|
|
5
|
+
from .core.tree_builder import run_tree_builder
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def main():
|
|
9
|
+
parser = argparse.ArgumentParser(description="Nova-Forge: 企业级项目锻造炉")
|
|
10
|
+
subparsers = parser.add_subparsers(dest="command", help="可用命令")
|
|
11
|
+
|
|
12
|
+
# 命令 1: 初始化配置 (原来的 nova_forge)
|
|
13
|
+
subparsers.add_parser("init", help="初始化项目配置文件 (pyproject, README, LICENSE 等)")
|
|
14
|
+
|
|
15
|
+
# 命令 2: 根据 txt 生成目录树 (原来的 nova_tree)
|
|
16
|
+
tree_parser = subparsers.add_parser("tree", help="根据 project_structure.txt 生成项目目录")
|
|
17
|
+
tree_parser.add_argument("-f", "--file", default="project_structure.txt", help="指定结构树文件")
|
|
18
|
+
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
if args.command == "init":
|
|
22
|
+
run_scaffolder()
|
|
23
|
+
elif args.command == "tree":
|
|
24
|
+
run_tree_builder(Path(args.file))
|
|
25
|
+
else:
|
|
26
|
+
parser.print_help()
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
if __name__ == "__main__":
|
|
30
|
+
main()
|
|
File without changes
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# src/nova_forge/core/scaffolder.py
|
|
2
|
+
import datetime
|
|
3
|
+
import os
|
|
4
|
+
from string import Template
|
|
5
|
+
|
|
6
|
+
# ✅ 从 templates.py 导入所有模版
|
|
7
|
+
from .templates import (
|
|
8
|
+
DEFAULT_GITHUB_USER, DEFAULT_REAL_NAME, DEFAULT_DESC_EN, DEFAULT_DESC_ZH, DEFAULT_DEPS,
|
|
9
|
+
PYPROJECT_TMPL, LICENSE_TMPL, README_EN_TMPL, README_ZH_TMPL,
|
|
10
|
+
CHANGELOG_TMPL, INIT_PY_TMPL, WORKFLOW_TMPL, GITIGNORE_TMPL,
|
|
11
|
+
INIT_GIT_TMPL, BUILD_PROD_TMPL, CB
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def ask(prompt, default):
|
|
16
|
+
res = input(f"{prompt} [{default}]: ").strip()
|
|
17
|
+
return res if res else default
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def run_scaffolder():
|
|
21
|
+
print("🔥 欢迎使用 Nova-Forge 终极项目锻造炉\n")
|
|
22
|
+
|
|
23
|
+
# 1. 收集信息
|
|
24
|
+
project_name = ask("📦 项目名 (PyPI包名, 如 nova-chat)", "my-awesome-project")
|
|
25
|
+
repo_name = ask("📁 GitHub仓库名 (如 Nova-Agent)", project_name)
|
|
26
|
+
github_user = ask("👤 GitHub用户名", DEFAULT_GITHUB_USER)
|
|
27
|
+
real_name = ask("✍️ 真实姓名 (用于 LICENSE)", DEFAULT_REAL_NAME)
|
|
28
|
+
desc_en = ask("🇬🇧 英文描述", DEFAULT_DESC_EN)
|
|
29
|
+
desc_zh = ask("🇨🇳 中文描述", DEFAULT_DESC_ZH)
|
|
30
|
+
|
|
31
|
+
year = datetime.datetime.now().year
|
|
32
|
+
month = datetime.datetime.now().strftime("%m")
|
|
33
|
+
day = datetime.datetime.now().strftime("%d")
|
|
34
|
+
module_name = project_name.replace('-', '_')
|
|
35
|
+
|
|
36
|
+
# 用于注入的全局字典
|
|
37
|
+
context = {
|
|
38
|
+
"PROJECT_NAME": project_name,
|
|
39
|
+
"REPO_NAME": repo_name,
|
|
40
|
+
"GITHUB_USER": github_user,
|
|
41
|
+
"REAL_NAME": real_name,
|
|
42
|
+
"DESC_EN": desc_en,
|
|
43
|
+
"DESC_ZH": desc_zh,
|
|
44
|
+
"YEAR": year,
|
|
45
|
+
"MONTH": month,
|
|
46
|
+
"DAY": day,
|
|
47
|
+
"MODULE_NAME": module_name,
|
|
48
|
+
"DEPS": DEFAULT_DEPS,
|
|
49
|
+
"CB": CB # ✅ 核心:将防截断的反引号传入
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
def write_file(path, tmpl_string, custom_context=None):
|
|
53
|
+
"""通用写文件函数"""
|
|
54
|
+
ctx = {**context, **(custom_context or {})}
|
|
55
|
+
content = Template(tmpl_string).safe_substitute(ctx)
|
|
56
|
+
path = Template(path).safe_substitute(ctx)
|
|
57
|
+
|
|
58
|
+
if not path.endswith("/"):
|
|
59
|
+
os.makedirs(os.path.dirname(path) or ".", exist_ok=True)
|
|
60
|
+
else:
|
|
61
|
+
os.makedirs(path, exist_ok=True)
|
|
62
|
+
|
|
63
|
+
if not path.endswith("/"):
|
|
64
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
65
|
+
f.write(content)
|
|
66
|
+
print(f"✅ 已生成: {path}")
|
|
67
|
+
|
|
68
|
+
# 2. 执行生成
|
|
69
|
+
write_file("pyproject.toml", PYPROJECT_TMPL)
|
|
70
|
+
write_file("LICENSE", LICENSE_TMPL)
|
|
71
|
+
write_file("README.md", README_EN_TMPL)
|
|
72
|
+
write_file("README_zh.md", README_ZH_TMPL)
|
|
73
|
+
write_file("CHANGELOG.md", CHANGELOG_TMPL)
|
|
74
|
+
write_file(".gitignore", GITIGNORE_TMPL)
|
|
75
|
+
|
|
76
|
+
# 骨架文件
|
|
77
|
+
os.makedirs("src", exist_ok=True)
|
|
78
|
+
write_file("src/$MODULE_NAME/__init__.py", INIT_PY_TMPL)
|
|
79
|
+
|
|
80
|
+
# 工具脚本
|
|
81
|
+
write_file("init_git.py", INIT_GIT_TMPL)
|
|
82
|
+
write_file("build_prod.bat", BUILD_PROD_TMPL)
|
|
83
|
+
write_file(".github/workflows/publish.yml", WORKFLOW_TMPL)
|
|
84
|
+
|
|
85
|
+
# 空的 project_structure.txt
|
|
86
|
+
with open("project_structure.txt", "w", encoding="utf-8") as f:
|
|
87
|
+
pass
|
|
88
|
+
print("✅ 已生成: project_structure.txt (空文件)")
|
|
89
|
+
|
|
90
|
+
print("\n🎉 企业级全家桶生成完毕!")
|
|
91
|
+
print("👉 已包含:动态版本号、CHANGELOG、GitHub Actions 自动发版!")
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Nova-Forge 模版库
|
|
4
|
+
集中管理所有的文件生成模版,保持 scaffolder.py 的整洁。
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# ==================== 默认配置 ====================
|
|
8
|
+
DEFAULT_GITHUB_USER = "eagleye-china"
|
|
9
|
+
DEFAULT_REAL_NAME = "单保录"
|
|
10
|
+
DEFAULT_DESC_EN = "A stunning, GPU-accelerated desktop application for PySide6."
|
|
11
|
+
DEFAULT_DESC_ZH = "基于 PySide6 和 QML 打造的炫酷桌面级应用框架。"
|
|
12
|
+
DEFAULT_DEPS = '["PySide6>=6.6", "openai>=1.0", "markdown>=3.5"]'
|
|
13
|
+
|
|
14
|
+
# ✅ 核心防截断变量
|
|
15
|
+
CB = "`" * 3
|
|
16
|
+
|
|
17
|
+
# ==================== pyproject.toml ====================
|
|
18
|
+
PYPROJECT_TMPL = """# ==========================================================
|
|
19
|
+
# 🚀 $PROJECT_NAME 企业级打包配置
|
|
20
|
+
# ==========================================================
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["setuptools>=68.2", "wheel"]
|
|
23
|
+
build-backend = "setuptools.build_meta"
|
|
24
|
+
|
|
25
|
+
[project]
|
|
26
|
+
name = "$PROJECT_NAME"
|
|
27
|
+
version = "0.1.0"
|
|
28
|
+
description = "$DESC_EN"
|
|
29
|
+
readme = "README.md"
|
|
30
|
+
requires-python = ">=3.10"
|
|
31
|
+
license = "MIT"
|
|
32
|
+
authors = [{ name = "$GITHUB_USER", email = "zdt168@sina.cn" }]
|
|
33
|
+
dependencies = $DEPS
|
|
34
|
+
|
|
35
|
+
[project.urls]
|
|
36
|
+
Homepage = "https://github.com/$GITHUB_USER/$REPO_NAME"
|
|
37
|
+
Repository = "https://github.com/$GITHUB_USER/$REPO_NAME"
|
|
38
|
+
Issues = "https://github.com/$GITHUB_USER/$REPO_NAME/issues"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.package-data]
|
|
44
|
+
"$MODULE_NAME" = [
|
|
45
|
+
"ui/*.qml",
|
|
46
|
+
"qml/*.qml",
|
|
47
|
+
"resources/*"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[tool.ruff]
|
|
51
|
+
line-length = 100
|
|
52
|
+
target-version = "py310"
|
|
53
|
+
|
|
54
|
+
[tool.ruff.lint]
|
|
55
|
+
select = ["E", "F", "I", "N", "W", "UP", "B", "C4", "SIM"]
|
|
56
|
+
ignore = ["N802", "E501"]
|
|
57
|
+
|
|
58
|
+
[tool.mypy]
|
|
59
|
+
python_version = "3.10"
|
|
60
|
+
strict = true
|
|
61
|
+
ignore_missing_imports = true
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
# ==================== LICENSE ====================
|
|
65
|
+
LICENSE_TMPL = """MIT License
|
|
66
|
+
|
|
67
|
+
Copyright (c) $YEAR $REAL_NAME ($GITHUB_USER)
|
|
68
|
+
|
|
69
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
70
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
71
|
+
in the Software without restriction, including without limitation the rights
|
|
72
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
73
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
74
|
+
furnished to do so, subject to the following conditions:
|
|
75
|
+
|
|
76
|
+
The above copyright notice and this permission notice shall be included in all
|
|
77
|
+
copies or substantial portions of the Software.
|
|
78
|
+
|
|
79
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
80
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
81
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
82
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
83
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
84
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
85
|
+
SOFTWARE.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
# ==================== README.md ====================
|
|
89
|
+
README_EN_TMPL = """# $PROJECT_NAME 🌟
|
|
90
|
+
|
|
91
|
+
> **$DESC_EN**
|
|
92
|
+
|
|
93
|
+
**English** | [简体中文](https://github.com/$GITHUB_USER/$REPO_NAME/blob/main/README_zh.md)
|
|
94
|
+
|
|
95
|
+
[](https://pypi.org/project/$PROJECT_NAME/)
|
|
96
|
+
[](https://pypi.org/project/$PROJECT_NAME/)
|
|
97
|
+
[](https://github.com/$GITHUB_USER/$REPO_NAME/blob/main/LICENSE)
|
|
98
|
+
[](https://github.com/$GITHUB_USER/$REPO_NAME)
|
|
99
|
+
|
|
100
|
+
## 🤔 Why $PROJECT_NAME?
|
|
101
|
+
|
|
102
|
+
- 🚀 **Blazing Fast**: Built with pure PySide6 and QML.
|
|
103
|
+
- 🎨 **Stunning UI**: Native dark mode, smooth animations.
|
|
104
|
+
|
|
105
|
+
## 📦 Installation
|
|
106
|
+
|
|
107
|
+
${CB}bash
|
|
108
|
+
pip install $PROJECT_NAME
|
|
109
|
+
${CB}
|
|
110
|
+
|
|
111
|
+
## 🚀 Quick Start
|
|
112
|
+
|
|
113
|
+
${CB}python
|
|
114
|
+
from $MODULE_NAME import __version__
|
|
115
|
+
print(f"Version: {__version__}")
|
|
116
|
+
${CB}
|
|
117
|
+
|
|
118
|
+
## 📜 License
|
|
119
|
+
|
|
120
|
+
MIT License. See [LICENSE](https://github.com/$GITHUB_USER/$REPO_NAME/blob/main/LICENSE).
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
# ==================== README_zh.md ====================
|
|
124
|
+
README_ZH_TMPL = """# $PROJECT_NAME 🌟
|
|
125
|
+
|
|
126
|
+
> **$DESC_ZH**
|
|
127
|
+
|
|
128
|
+
[English](https://github.com/$GITHUB_USER/$REPO_NAME/blob/main/README.md) | **简体中文**
|
|
129
|
+
|
|
130
|
+
[](https://pypi.org/project/$PROJECT_NAME/)
|
|
131
|
+
[](https://pypi.org/project/$PROJECT_NAME/)
|
|
132
|
+
[](https://github.com/$GITHUB_USER/$REPO_NAME/blob/main/LICENSE)
|
|
133
|
+
[](https://github.com/$GITHUB_USER/$REPO_NAME)
|
|
134
|
+
|
|
135
|
+
## 🤔 为什么选择 $PROJECT_NAME?
|
|
136
|
+
|
|
137
|
+
- 🚀 **极致性能**:基于原生 PySide6 和 QML,拒绝笨重的 WebView。
|
|
138
|
+
- 🎨 **顶级颜值**:原生暗黑模式,丝滑动画,像素级布局。
|
|
139
|
+
|
|
140
|
+
## 📦 安装
|
|
141
|
+
|
|
142
|
+
${CB}bash
|
|
143
|
+
pip install $PROJECT_NAME
|
|
144
|
+
${CB}
|
|
145
|
+
|
|
146
|
+
## 📜 开源协议
|
|
147
|
+
|
|
148
|
+
MIT License。
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
# ==================== CHANGELOG.md ====================
|
|
152
|
+
CHANGELOG_TMPL = """# Changelog
|
|
153
|
+
所有版本变更记录,遵循 [Semantic Versioning](https://semver.org/) 规范。
|
|
154
|
+
|
|
155
|
+
## [0.1.0] - $YEAR-$MONTH-$DAY
|
|
156
|
+
### ✨ Features
|
|
157
|
+
- 初始版本发布,搭建完整的企业级工程骨架。
|
|
158
|
+
- 完成 PyPI 和 GitHub 的发版流水线配置。
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
# ==================== __init__.py ====================
|
|
162
|
+
INIT_PY_TMPL = """# -*- coding: utf-8 -*-
|
|
163
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
164
|
+
|
|
165
|
+
try:
|
|
166
|
+
__version__: str = version("$PROJECT_NAME")
|
|
167
|
+
except PackageNotFoundError:
|
|
168
|
+
__version__ = "0.1.0"
|
|
169
|
+
|
|
170
|
+
__all__ = ["__version__"]
|
|
171
|
+
"""
|
|
172
|
+
|
|
173
|
+
# ==================== GitHub Actions ====================
|
|
174
|
+
WORKFLOW_TMPL = """name: Publish to PyPI
|
|
175
|
+
|
|
176
|
+
on:
|
|
177
|
+
push:
|
|
178
|
+
tags:
|
|
179
|
+
- 'v*'
|
|
180
|
+
|
|
181
|
+
jobs:
|
|
182
|
+
build-and-publish:
|
|
183
|
+
runs-on: ubuntu-latest
|
|
184
|
+
environment: pypi
|
|
185
|
+
permissions:
|
|
186
|
+
id-token: write
|
|
187
|
+
steps:
|
|
188
|
+
- uses: actions/checkout@v4
|
|
189
|
+
- name: Set up Python
|
|
190
|
+
uses: actions/setup-python@v5
|
|
191
|
+
with:
|
|
192
|
+
python-version: '3.10'
|
|
193
|
+
- name: Build package
|
|
194
|
+
run: |
|
|
195
|
+
python -m pip install --upgrade pip
|
|
196
|
+
pip install build
|
|
197
|
+
python -m build
|
|
198
|
+
- name: Publish to PyPI
|
|
199
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
200
|
+
"""
|
|
201
|
+
|
|
202
|
+
# ==================== .gitignore ====================
|
|
203
|
+
GITIGNORE_TMPL = """# Python
|
|
204
|
+
__pycache__/
|
|
205
|
+
*.py[cod]
|
|
206
|
+
build/
|
|
207
|
+
dist/
|
|
208
|
+
*.egg-info/
|
|
209
|
+
|
|
210
|
+
# IDE
|
|
211
|
+
.idea/
|
|
212
|
+
.vscode/
|
|
213
|
+
.DS_Store
|
|
214
|
+
|
|
215
|
+
# Environments
|
|
216
|
+
.env
|
|
217
|
+
.venv
|
|
218
|
+
venv/
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
# ==================== init_git.py ====================
|
|
222
|
+
INIT_GIT_TMPL = """import os
|
|
223
|
+
import re
|
|
224
|
+
import subprocess
|
|
225
|
+
import sys
|
|
226
|
+
from pathlib import Path
|
|
227
|
+
|
|
228
|
+
GITHUB_USER = "$GITHUB_USER"
|
|
229
|
+
|
|
230
|
+
def run(cmd, ignore_error=False):
|
|
231
|
+
print(f"👉 正在执行: {' '.join(cmd)}")
|
|
232
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
233
|
+
if result.returncode != 0 and not ignore_error:
|
|
234
|
+
print(f"❌ 错误: {result.stderr}")
|
|
235
|
+
raise RuntimeError(result.stderr)
|
|
236
|
+
if result.stdout.strip():
|
|
237
|
+
print(result.stdout.strip())
|
|
238
|
+
return result
|
|
239
|
+
|
|
240
|
+
def get_project_info():
|
|
241
|
+
current_dir = Path.cwd()
|
|
242
|
+
# ✅ 修复:直接使用生成时写死的 REPO_NAME,防止路径错乱
|
|
243
|
+
repo_name = "$REPO_NAME"
|
|
244
|
+
project_name = "$PROJECT_NAME"
|
|
245
|
+
version = "0.1.0"
|
|
246
|
+
toml_path = current_dir / "pyproject.toml"
|
|
247
|
+
if toml_path.exists():
|
|
248
|
+
with open(toml_path, "r", encoding="utf-8") as f:
|
|
249
|
+
content = f.read()
|
|
250
|
+
# ✅ 修复正则转义问题
|
|
251
|
+
name_match = re.search(r'name\s*=\s*["\']([^"\']+)["\']', content)
|
|
252
|
+
if name_match: project_name = name_match.group(1)
|
|
253
|
+
|
|
254
|
+
version_match = re.search(r'version\s*=\s*["\']([^"\']+)["\']', content)
|
|
255
|
+
if version_match: version = version_match.group(1)
|
|
256
|
+
return repo_name, project_name, version
|
|
257
|
+
|
|
258
|
+
def main():
|
|
259
|
+
repo_name, project_name, version = get_project_info()
|
|
260
|
+
remote_url = f"git@github.com:{GITHUB_USER}/{repo_name}.git"
|
|
261
|
+
tag_name = f"v{version}"
|
|
262
|
+
|
|
263
|
+
print(f"🚀 开始自动初始化并发布【{project_name}】 (v{version})...")
|
|
264
|
+
|
|
265
|
+
if not os.path.exists(".git"):
|
|
266
|
+
run(["git", "init"])
|
|
267
|
+
run(["git", "branch", "-M", "main"])
|
|
268
|
+
run(["git", "config", "core.autocrlf", "false"])
|
|
269
|
+
run(["git", "add", "."])
|
|
270
|
+
run(["git", "commit", "-m", f"feat: initial release of {project_name} v{version}"])
|
|
271
|
+
print("✅ 本地 Git 初始化完成!")
|
|
272
|
+
else:
|
|
273
|
+
print("✅ 已检测到 Git 仓库,跳过初始化。")
|
|
274
|
+
|
|
275
|
+
try:
|
|
276
|
+
remotes = subprocess.run(["git", "remote"], capture_output=True, text=True).stdout
|
|
277
|
+
if "origin" not in remotes:
|
|
278
|
+
run(["git", "remote", "add", "origin", remote_url])
|
|
279
|
+
print(f"✅ 已关联远程仓库: {remote_url}")
|
|
280
|
+
else:
|
|
281
|
+
current_url = subprocess.run(["git", "remote", "get-url", "origin"], capture_output=True, text=True).stdout.strip()
|
|
282
|
+
if current_url != remote_url:
|
|
283
|
+
run(["git", "remote", "set-url", "origin", remote_url])
|
|
284
|
+
print(f"✅ 已更新远程仓库地址: {remote_url}")
|
|
285
|
+
else:
|
|
286
|
+
print(f"✅ 远程仓库已配置正确,无需修改。")
|
|
287
|
+
except Exception as e:
|
|
288
|
+
print(f"❌ 配置远程仓库失败: {e}")
|
|
289
|
+
|
|
290
|
+
print("\\n📤 正在推送代码到 GitHub...")
|
|
291
|
+
try:
|
|
292
|
+
run(["git", "push", "-f", "-u", "origin", "main"])
|
|
293
|
+
print("✅ 代码推送成功!")
|
|
294
|
+
except RuntimeError:
|
|
295
|
+
print("❌ 推送失败,请检查 SSH Key 或网络连接!")
|
|
296
|
+
sys.exit(1)
|
|
297
|
+
|
|
298
|
+
print(f"\\n🏷️ 准备发布版本 {tag_name}...")
|
|
299
|
+
try:
|
|
300
|
+
tags = subprocess.run(["git", "tag"], capture_output=True, text=True).stdout
|
|
301
|
+
if tag_name not in tags.split():
|
|
302
|
+
run(["git", "tag", tag_name])
|
|
303
|
+
print(f"✅ 标签 {tag_name} 创建成功。")
|
|
304
|
+
else:
|
|
305
|
+
print(f"✅ 标签 {tag_name} 已存在。")
|
|
306
|
+
run(["git", "push", "origin", tag_name])
|
|
307
|
+
print(f"🚀 标签 {tag_name} 推送成功!GitHub Actions 发版流水线已触发!")
|
|
308
|
+
except Exception as e:
|
|
309
|
+
print(f"❌ 标签推送失败: {e}")
|
|
310
|
+
|
|
311
|
+
print("\\n🎉 全自动发布流程结束!请前往 GitHub 的 Actions 页面查看发版进度。")
|
|
312
|
+
|
|
313
|
+
if __name__ == "__main__":
|
|
314
|
+
main()
|
|
315
|
+
"""
|
|
316
|
+
|
|
317
|
+
# ==================== build_prod.bat ====================
|
|
318
|
+
BUILD_PROD_TMPL = """@echo off
|
|
319
|
+
chcp 65001 >nul
|
|
320
|
+
echo =========================================
|
|
321
|
+
echo $PROJECT_NAME 打包与发布
|
|
322
|
+
echo =========================================
|
|
323
|
+
|
|
324
|
+
if exist dist rmdir /s /q dist
|
|
325
|
+
if exist build rmdir /s /q build
|
|
326
|
+
if exist src\\*.egg-info rmdir /s /q src\\*.egg-info
|
|
327
|
+
|
|
328
|
+
python -m build
|
|
329
|
+
python -m twine check dist/*
|
|
330
|
+
python -m twine upload dist/*
|
|
331
|
+
pause
|
|
332
|
+
"""
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
Nova Tree 🌲
|
|
4
|
+
============
|
|
5
|
+
项目目录结构生成器 (完美防套娃、智能识别版)
|
|
6
|
+
根据同目录下的 `project_structure.txt`,一键生成完整的工程目录。
|
|
7
|
+
"""
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# ==========================================
|
|
14
|
+
# 📦 数据模型
|
|
15
|
+
# ==========================================
|
|
16
|
+
@dataclass(slots=True)
|
|
17
|
+
class Node:
|
|
18
|
+
name: str
|
|
19
|
+
is_dir: bool
|
|
20
|
+
level: int = 0
|
|
21
|
+
children: list["Node"] = field(default_factory=list)
|
|
22
|
+
|
|
23
|
+
def add_child(self, child: "Node") -> None:
|
|
24
|
+
self.children.append(child)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@dataclass(slots=True)
|
|
28
|
+
class BuildResult:
|
|
29
|
+
created_dirs: list[Path] = field(default_factory=list)
|
|
30
|
+
created_files: list[Path] = field(default_factory=list)
|
|
31
|
+
skipped: list[Path] = field(default_factory=list)
|
|
32
|
+
failed: list[tuple[Path, str]] = field(default_factory=list)
|
|
33
|
+
|
|
34
|
+
def summary(self) -> str:
|
|
35
|
+
return (
|
|
36
|
+
f"📁 新建目录: {len(self.created_dirs)} | "
|
|
37
|
+
f"📄 新建文件: {len(self.created_files)} | "
|
|
38
|
+
f"⏭️ 跳过已存在: {len(self.skipped)} | "
|
|
39
|
+
f"❌ 失败: {len(self.failed)}"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# ==========================================
|
|
44
|
+
# 🔍 结构解析器
|
|
45
|
+
# ==========================================
|
|
46
|
+
class StructureParser:
|
|
47
|
+
_TREE_MARKERS = ("├── ", "└── ", "├─ ", "└─ ", "|--- ", "|-- ", "└─ ")
|
|
48
|
+
_INDENT_CHARS = set("│ ├└─|")
|
|
49
|
+
|
|
50
|
+
def parse(self, text: str) -> list[Node]:
|
|
51
|
+
roots: list[Node] = []
|
|
52
|
+
stack: list[tuple[int, Node]] = []
|
|
53
|
+
|
|
54
|
+
for raw in text.splitlines():
|
|
55
|
+
parsed = self._parse_line(raw)
|
|
56
|
+
if parsed is None:
|
|
57
|
+
continue
|
|
58
|
+
|
|
59
|
+
level, name, is_dir = parsed
|
|
60
|
+
node = Node(name=name, is_dir=is_dir, level=level)
|
|
61
|
+
|
|
62
|
+
while stack and stack[-1][0] >= level:
|
|
63
|
+
stack.pop()
|
|
64
|
+
|
|
65
|
+
if stack:
|
|
66
|
+
stack[-1][1].add_child(node)
|
|
67
|
+
else:
|
|
68
|
+
roots.append(node)
|
|
69
|
+
|
|
70
|
+
stack.append((level, node))
|
|
71
|
+
|
|
72
|
+
return roots
|
|
73
|
+
|
|
74
|
+
def _parse_line(self, line: str) -> tuple[int, str, bool] | None:
|
|
75
|
+
line = line.rstrip()
|
|
76
|
+
if not line.strip() or all(c in "│ ├└─|" for c in line.strip()):
|
|
77
|
+
return None
|
|
78
|
+
if "|" in line and not any(m in line for m in self._TREE_MARKERS):
|
|
79
|
+
return None
|
|
80
|
+
if line.lstrip().startswith(("```", "---", "===")):
|
|
81
|
+
return None
|
|
82
|
+
|
|
83
|
+
for marker in self._TREE_MARKERS:
|
|
84
|
+
idx = line.find(marker)
|
|
85
|
+
if idx >= 0:
|
|
86
|
+
prefix = line[:idx]
|
|
87
|
+
if all(ch in self._INDENT_CHARS for ch in prefix):
|
|
88
|
+
level = idx // 4 + 1
|
|
89
|
+
name = line[idx + len(marker):]
|
|
90
|
+
clean_name = self._clean_name(name)
|
|
91
|
+
if clean_name:
|
|
92
|
+
return level, clean_name, self._is_dir(name)
|
|
93
|
+
|
|
94
|
+
if line[0].isspace() and not line.lstrip().startswith(("def ", "class ", "import ", "from ", "@")):
|
|
95
|
+
stripped = line.lstrip()
|
|
96
|
+
level = (len(line) - len(stripped)) // 4
|
|
97
|
+
clean_name = self._clean_name(stripped)
|
|
98
|
+
if clean_name:
|
|
99
|
+
return level, clean_name, self._is_dir(stripped)
|
|
100
|
+
|
|
101
|
+
clean_name = self._clean_name(line)
|
|
102
|
+
if clean_name:
|
|
103
|
+
return 0, clean_name, self._is_dir(line)
|
|
104
|
+
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
@staticmethod
|
|
108
|
+
def _clean_name(name: str) -> str:
|
|
109
|
+
name = name.split("#")[0].strip()
|
|
110
|
+
name = name.rstrip("/\\:").strip()
|
|
111
|
+
name = re.sub(r'[<>:"|?*]', '_', name)
|
|
112
|
+
return name.strip()
|
|
113
|
+
|
|
114
|
+
@staticmethod
|
|
115
|
+
def _is_dir(name: str) -> bool:
|
|
116
|
+
name = name.rstrip()
|
|
117
|
+
if name.endswith(("/", "\\")):
|
|
118
|
+
return True
|
|
119
|
+
last = name.split("/")[-1].split("\\")[-1]
|
|
120
|
+
return "." not in last
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
# ==========================================
|
|
124
|
+
# 🛠️ 项目构建器
|
|
125
|
+
# ==========================================
|
|
126
|
+
class ProjectBuilder:
|
|
127
|
+
def build(self, roots: list[Node], target: Path):
|
|
128
|
+
result = BuildResult()
|
|
129
|
+
target = Path(target).resolve()
|
|
130
|
+
|
|
131
|
+
current_dir_name = target.name
|
|
132
|
+
nodes_to_walk = []
|
|
133
|
+
real_root = target
|
|
134
|
+
|
|
135
|
+
# ✅ 核心逻辑 1:智能防套娃
|
|
136
|
+
if len(roots) == 1 and roots[0].is_dir:
|
|
137
|
+
if roots[0].name == current_dir_name:
|
|
138
|
+
print(f"🔍 检测到当前目录名与根节点名一致 ('{current_dir_name}'),跳过根目录创建,直接生成子结构。")
|
|
139
|
+
nodes_to_walk = roots[0].children
|
|
140
|
+
else:
|
|
141
|
+
print(f"🔍 根节点名 ('{roots[0].name}') 与当前目录 ('{current_dir_name}') 不一致,将创建新根节点。")
|
|
142
|
+
real_root = target / roots[0].name
|
|
143
|
+
real_root.mkdir(parents=True, exist_ok=True)
|
|
144
|
+
nodes_to_walk = roots[0].children
|
|
145
|
+
else:
|
|
146
|
+
print(f"🔍 检测到多个顶级节点或散落文件,直接在当前目录生成。")
|
|
147
|
+
nodes_to_walk = roots
|
|
148
|
+
|
|
149
|
+
# 1. 递归生成节点
|
|
150
|
+
for node in nodes_to_walk:
|
|
151
|
+
self._create_node(node, real_root, result)
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
def _create_node(self, node: Node, parent: Path, result: BuildResult):
|
|
156
|
+
path = parent / node.name
|
|
157
|
+
|
|
158
|
+
if node.is_dir:
|
|
159
|
+
if not path.exists():
|
|
160
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
161
|
+
result.created_dirs.append(path)
|
|
162
|
+
else:
|
|
163
|
+
result.skipped.append(path)
|
|
164
|
+
|
|
165
|
+
# 自动补全 __init__.py (如果是 Python 包且有子节点)
|
|
166
|
+
if node.children and not any(c.name == "__init__.py" for c in node.children):
|
|
167
|
+
init_file = path / "__init__.py"
|
|
168
|
+
self._write_file(init_file, "", result)
|
|
169
|
+
|
|
170
|
+
for child in node.children:
|
|
171
|
+
self._create_node(child, path, result)
|
|
172
|
+
else:
|
|
173
|
+
self._write_file(path, "", result)
|
|
174
|
+
|
|
175
|
+
def _write_file(self, path: Path, content: str, result: BuildResult):
|
|
176
|
+
if path.exists():
|
|
177
|
+
result.skipped.append(path)
|
|
178
|
+
return
|
|
179
|
+
|
|
180
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
181
|
+
if not content:
|
|
182
|
+
content = self._default_content(path)
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
path.write_text(content, encoding="utf-8")
|
|
186
|
+
result.created_files.append(path)
|
|
187
|
+
except Exception as e:
|
|
188
|
+
result.failed.append((path, str(e)))
|
|
189
|
+
|
|
190
|
+
@staticmethod
|
|
191
|
+
def _default_content(path: Path) -> str:
|
|
192
|
+
name = path.name
|
|
193
|
+
suffix = path.suffix.lower()
|
|
194
|
+
|
|
195
|
+
if name == "__init__.py": return ""
|
|
196
|
+
if suffix == ".py":
|
|
197
|
+
return f'# -*- coding: utf-8 -*-\n# {path.stem}.py\n\n'
|
|
198
|
+
if suffix == ".md":
|
|
199
|
+
return f"# {path.stem}\n\n> 文档生成中...\n"
|
|
200
|
+
if suffix == ".qrc":
|
|
201
|
+
return '<RCC>\n <qresource prefix="/">\n </qresource>\n</RCC>\n'
|
|
202
|
+
if suffix == ".qml":
|
|
203
|
+
return 'import QtQuick\n\nItem {\n width: 100\n height: 100\n}\n'
|
|
204
|
+
return ""
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ==========================================
|
|
208
|
+
# 🚀 主程序入口
|
|
209
|
+
# ==========================================
|
|
210
|
+
# ==========================================
|
|
211
|
+
# 🚀 主程序入口 (改造为可传参函数)
|
|
212
|
+
# ==========================================
|
|
213
|
+
def run_tree_builder(file_path: Path):
|
|
214
|
+
# 确保传入的是 Path 对象
|
|
215
|
+
file_path = Path(file_path)
|
|
216
|
+
|
|
217
|
+
# ✅ 核心逻辑 2:空文件与缺失检测
|
|
218
|
+
if not file_path.exists():
|
|
219
|
+
print(f"❌ 错误: 找不到 [{file_path.name}] 文件!")
|
|
220
|
+
print("💡 请先创建该文件并填入目录树。")
|
|
221
|
+
return
|
|
222
|
+
|
|
223
|
+
content = file_path.read_text(encoding="utf-8").strip()
|
|
224
|
+
if not content:
|
|
225
|
+
print(f"❌ 错误: [{file_path.name}] 文件内容为空!")
|
|
226
|
+
print("💡 请写入类似以下的格式:\n项目名/\n├── src/\n│ └── main.py")
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
print("🔥 启动 Nova Tree 终极结构生成器...\n")
|
|
230
|
+
|
|
231
|
+
parser = StructureParser()
|
|
232
|
+
roots = parser.parse(content)
|
|
233
|
+
|
|
234
|
+
if not roots:
|
|
235
|
+
print("❌ 错误: 未能解析出有效目录,请检查 txt 语法格式。")
|
|
236
|
+
return
|
|
237
|
+
|
|
238
|
+
builder = ProjectBuilder()
|
|
239
|
+
result = builder.build(roots, target=Path.cwd())
|
|
240
|
+
|
|
241
|
+
print("\n" + "=" * 40)
|
|
242
|
+
print("🎉 构建执行完毕!")
|
|
243
|
+
print(result.summary())
|
|
244
|
+
if result.failed:
|
|
245
|
+
print("\n❌ 失败列表:")
|
|
246
|
+
for path, err in result.failed:
|
|
247
|
+
print(f" - {path}: {err}")
|
|
248
|
+
print("=" * 40)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nova-forge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds.
|
|
5
|
+
Author-email: eagleye-china <zdt168@sina.cn>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/eagleye-china/Nova-Forge
|
|
8
|
+
Project-URL: Repository, https://github.com/eagleye-china/Nova-Forge
|
|
9
|
+
Project-URL: Issues, https://github.com/eagleye-china/Nova-Forge/issues
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: build>=1.0.0
|
|
18
|
+
Requires-Dist: twine>=5.0.0
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# Nova-Forge 🛠️
|
|
22
|
+
|
|
23
|
+
> **The ultimate project forging tool for PySide6 ecosystems. Scaffold enterprise-grade architectures in seconds.**
|
|
24
|
+
|
|
25
|
+
**English** | [简体中文](https://github.com/eagleye-china/Nova-Forge/blob/main/README_zh.md)
|
|
26
|
+
|
|
27
|
+
[](https://pypi.org/project/nova-forge/)
|
|
28
|
+
[](https://pypi.org/project/nova-forge/)
|
|
29
|
+
[](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE)
|
|
30
|
+
[](https://github.com/eagleye-china/Nova-Forge)
|
|
31
|
+
|
|
32
|
+
**Nova-Forge** is a blazing-fast CLI scaffolding tool designed to eliminate the boilerplate of modern Python desktop applications.
|
|
33
|
+
|
|
34
|
+
Stop copying and pasting `pyproject.toml`, fumbling with MIT licenses, or manually creating hundreds of folders. With a single command, Nova-Forge generates a complete, production-ready `src-layout` project equipped with CI/CD, bilingual docs, and PyPI packaging scripts.
|
|
35
|
+
|
|
36
|
+
## ✨ Features
|
|
37
|
+
|
|
38
|
+
- 🚀 **`nova-forge init`**: Generates `pyproject.toml`, bilingual `README.md`, `LICENSE`, and a GitHub Actions workflow for automatic PyPI publishing.
|
|
39
|
+
- 🌲 **`nova-forge tree`**: Builds an entire directory structure instantly from a simple text-based tree diagram.
|
|
40
|
+
- 🤖 **AI-Ready**: Perfect for pairing with AI coding assistants (like DeepSeek or ChatGPT) to instantly materialize AI-generated architectures.
|
|
41
|
+
|
|
42
|
+
## 📦 Installation
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install nova-forge
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## 🛠️ Usage
|
|
49
|
+
|
|
50
|
+
### 1. Initialize Project Configuration
|
|
51
|
+
Run this in your empty project folder. It will interactively ask for your project details and generate all necessary metadata.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
nova-forge init
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 2. Build Directory Structure
|
|
58
|
+
Got a project layout from your AI architect? Paste it into `project_structure.txt`:
|
|
59
|
+
|
|
60
|
+
```text
|
|
61
|
+
my-awesome-app/
|
|
62
|
+
├── src/
|
|
63
|
+
│ └── my_awesome_app/
|
|
64
|
+
│ ├── __init__.py
|
|
65
|
+
│ └── main.py
|
|
66
|
+
└── examples/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Then run:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
nova-forge tree
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
*Boom! Your entire folder structure is created with auto-generated `__init__.py` and boilerplate code.*
|
|
76
|
+
|
|
77
|
+
## 📜 License
|
|
78
|
+
|
|
79
|
+
MIT License. See [LICENSE](https://github.com/eagleye-china/Nova-Forge/blob/main/LICENSE) for details.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
src/nova_forge/__init__.py
|
|
5
|
+
src/nova_forge/cli.py
|
|
6
|
+
src/nova_forge.egg-info/PKG-INFO
|
|
7
|
+
src/nova_forge.egg-info/SOURCES.txt
|
|
8
|
+
src/nova_forge.egg-info/dependency_links.txt
|
|
9
|
+
src/nova_forge.egg-info/entry_points.txt
|
|
10
|
+
src/nova_forge.egg-info/requires.txt
|
|
11
|
+
src/nova_forge.egg-info/top_level.txt
|
|
12
|
+
src/nova_forge/assets/__init__.py
|
|
13
|
+
src/nova_forge/core/__init__.py
|
|
14
|
+
src/nova_forge/core/scaffolder.py
|
|
15
|
+
src/nova_forge/core/templates.py
|
|
16
|
+
src/nova_forge/core/tree_builder.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
nova_forge
|