gitinstall 1.1.0__py3-none-any.whl
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.
- gitinstall/__init__.py +61 -0
- gitinstall/_sdk.py +541 -0
- gitinstall/academic.py +831 -0
- gitinstall/admin.html +327 -0
- gitinstall/auto_update.py +384 -0
- gitinstall/autopilot.py +349 -0
- gitinstall/badge.py +476 -0
- gitinstall/checkpoint.py +330 -0
- gitinstall/cicd.py +499 -0
- gitinstall/clawhub.html +718 -0
- gitinstall/config_schema.py +353 -0
- gitinstall/db.py +984 -0
- gitinstall/db_backend.py +445 -0
- gitinstall/dep_chain.py +337 -0
- gitinstall/dependency_audit.py +1153 -0
- gitinstall/detector.py +542 -0
- gitinstall/doctor.py +493 -0
- gitinstall/education.py +869 -0
- gitinstall/enterprise.py +802 -0
- gitinstall/error_fixer.py +953 -0
- gitinstall/event_bus.py +251 -0
- gitinstall/executor.py +577 -0
- gitinstall/feature_flags.py +138 -0
- gitinstall/fetcher.py +921 -0
- gitinstall/huggingface.py +922 -0
- gitinstall/hw_detect.py +988 -0
- gitinstall/i18n.py +664 -0
- gitinstall/installer_registry.py +362 -0
- gitinstall/knowledge_base.py +379 -0
- gitinstall/license_check.py +605 -0
- gitinstall/llm.py +569 -0
- gitinstall/log.py +236 -0
- gitinstall/main.py +1408 -0
- gitinstall/mcp_agent.py +841 -0
- gitinstall/mcp_server.py +386 -0
- gitinstall/monorepo.py +810 -0
- gitinstall/multi_source.py +425 -0
- gitinstall/onboard.py +276 -0
- gitinstall/planner.py +222 -0
- gitinstall/planner_helpers.py +323 -0
- gitinstall/planner_known_projects.py +1010 -0
- gitinstall/planner_templates.py +996 -0
- gitinstall/remote_gpu.py +633 -0
- gitinstall/resilience.py +608 -0
- gitinstall/run_tests.py +572 -0
- gitinstall/skills.py +476 -0
- gitinstall/tool_schemas.py +324 -0
- gitinstall/trending.py +279 -0
- gitinstall/uninstaller.py +415 -0
- gitinstall/validate_top100.py +607 -0
- gitinstall/watchdog.py +180 -0
- gitinstall/web.py +1277 -0
- gitinstall/web_ui.html +2277 -0
- gitinstall-1.1.0.dist-info/METADATA +275 -0
- gitinstall-1.1.0.dist-info/RECORD +59 -0
- gitinstall-1.1.0.dist-info/WHEEL +5 -0
- gitinstall-1.1.0.dist-info/entry_points.txt +3 -0
- gitinstall-1.1.0.dist-info/licenses/LICENSE +21 -0
- gitinstall-1.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
"""
|
|
2
|
+
planner_templates.py - 各语言安装计划模板
|
|
3
|
+
==========================================
|
|
4
|
+
|
|
5
|
+
从 planner.py 拆分出来的所有语言模板方法(Mixin)。
|
|
6
|
+
SmartPlanner 继承 PlanTemplateMixin 来获得所有 _plan_* 方法。
|
|
7
|
+
|
|
8
|
+
支持的语言/生态:
|
|
9
|
+
Python, Python ML, Python+Rust, Conda,
|
|
10
|
+
Node.js, Docker, Rust, Go, CMake, Java, Make,
|
|
11
|
+
Ruby, PHP, .NET, Swift, Kotlin, Scala, Dart,
|
|
12
|
+
Elixir, Haskell, Lua, Perl, R, Julia, Zig,
|
|
13
|
+
Clojure, Meson, Shell
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import re
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Any
|
|
22
|
+
|
|
23
|
+
_THIS_DIR = str(Path(__file__).parent)
|
|
24
|
+
if _THIS_DIR not in sys.path:
|
|
25
|
+
sys.path.insert(0, _THIS_DIR)
|
|
26
|
+
|
|
27
|
+
from planner_known_projects import _KNOWN_PROJECTS
|
|
28
|
+
from planner_helpers import (
|
|
29
|
+
_os_type, _is_apple_silicon, _gpu_type, _cuda_major,
|
|
30
|
+
_has_pm, _has_runtime, _python_cmd, _pip_cmd,
|
|
31
|
+
_venv_activate, _dep_names, _dep_content,
|
|
32
|
+
_is_maturin_project, _preferred_java_version,
|
|
33
|
+
_has_haskell_cabal_file, _stack_resolver, _stack_lts_major,
|
|
34
|
+
_zig_minimum_version, _zig_fallback_version, _version_tuple,
|
|
35
|
+
_zig_uses_legacy_build_api, _haskell_system_packages,
|
|
36
|
+
_haskell_macos_env_prefix, _haskell_repo_template,
|
|
37
|
+
_torch_install_cmd, _node_pm,
|
|
38
|
+
_make_step, _get_gpu_name,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class PlanTemplateMixin:
|
|
43
|
+
"""所有语言安装模板方法的 Mixin 类"""
|
|
44
|
+
|
|
45
|
+
# 源信息(由 SmartPlanner.generate_plan 在调用模板前设置)
|
|
46
|
+
_source_path: str = "" # 非空 = 本地路径,跳过 git clone
|
|
47
|
+
_clone_url: str = "" # 非空 = 自定义 clone URL(非 GitHub)
|
|
48
|
+
|
|
49
|
+
def _get_clone_url(self, owner: str, repo: str) -> str:
|
|
50
|
+
"""获取 clone URL,优先使用自定义 URL,否则回退 GitHub"""
|
|
51
|
+
return self._clone_url or f"https://github.com/{owner}/{repo}.git"
|
|
52
|
+
|
|
53
|
+
def _clone_and_cd(self, owner: str, repo: str) -> list[dict]:
|
|
54
|
+
"""生成 clone + cd 步骤,本地路径时只生成 cd"""
|
|
55
|
+
if self._source_path:
|
|
56
|
+
return [_make_step(f"cd {self._source_path}", "进入项目目录")]
|
|
57
|
+
url = self._get_clone_url(owner, repo)
|
|
58
|
+
return [
|
|
59
|
+
_make_step(f"git clone --depth 1 {url}", "克隆代码"),
|
|
60
|
+
_make_step(f"cd {repo}", "进入目录"),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ─────────────────────────────────────────
|
|
65
|
+
# 策略 1:已知项目
|
|
66
|
+
# ─────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
def _plan_from_known(self, key: str, env: dict) -> dict:
|
|
69
|
+
p = _KNOWN_PROJECTS[key]
|
|
70
|
+
os_type = _os_type(env)
|
|
71
|
+
node_install, node_dev = _node_pm(env)
|
|
72
|
+
steps = []
|
|
73
|
+
|
|
74
|
+
# 通用步骤
|
|
75
|
+
for s in p.get("steps", []):
|
|
76
|
+
steps.append(self._resolve_step(s, env, node_install, node_dev))
|
|
77
|
+
|
|
78
|
+
# by_os 步骤
|
|
79
|
+
by_os = p.get("by_os", {})
|
|
80
|
+
os_steps = by_os.get(os_type) or by_os.get("linux", [])
|
|
81
|
+
for s in os_steps:
|
|
82
|
+
steps.append(self._resolve_step(s, env, node_install, node_dev))
|
|
83
|
+
|
|
84
|
+
# docker_preferred:有 Docker 走 Docker,没有走 pip
|
|
85
|
+
if p.get("by_platform") == "docker_preferred":
|
|
86
|
+
if _has_runtime(env, "docker"):
|
|
87
|
+
src = p.get("steps_docker", [])
|
|
88
|
+
else:
|
|
89
|
+
src = p.get("steps_pip", [])
|
|
90
|
+
steps = [self._resolve_step(s, env, node_install, node_dev) for s in src]
|
|
91
|
+
|
|
92
|
+
# 纯 Docker 项目(没有步骤但有 steps_docker)
|
|
93
|
+
if not steps and p.get("steps_docker"):
|
|
94
|
+
if _has_runtime(env, "docker"):
|
|
95
|
+
steps = [self._resolve_step(s, env, node_install, node_dev)
|
|
96
|
+
for s in p.get("steps_docker", [])]
|
|
97
|
+
else:
|
|
98
|
+
steps = [_make_step(
|
|
99
|
+
"# 此项目需要 Docker,请先安装 Docker Desktop",
|
|
100
|
+
"⚠️ Docker 未安装"
|
|
101
|
+
)]
|
|
102
|
+
|
|
103
|
+
launch = p.get("launch") or ""
|
|
104
|
+
launch = self._fill(launch, env, node_install, node_dev)
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
"project_name": key,
|
|
108
|
+
"steps": steps,
|
|
109
|
+
"launch_command": launch,
|
|
110
|
+
"notes": p.get("notes", ""),
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
# ─────────────────────────────────────────
|
|
114
|
+
# 策略 2:类型模板
|
|
115
|
+
# ─────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
def _plan_python_ml(self, owner, repo, env, dep_keys, types):
|
|
118
|
+
"""AI/ML Python 项目:必须 GPU 自适应"""
|
|
119
|
+
pip = _pip_cmd(env)
|
|
120
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
121
|
+
_make_step(f"{_python_cmd(env)} -m venv venv", "创建虚拟环境(隔离依赖,避免污染系统)"),
|
|
122
|
+
_make_step(_venv_activate(env), "激活虚拟环境"),
|
|
123
|
+
_make_step(_torch_install_cmd(env), f"安装 PyTorch(已自动适配:{_get_gpu_name(env)})"),
|
|
124
|
+
]
|
|
125
|
+
if "requirements.txt" in dep_keys:
|
|
126
|
+
steps.append(_make_step(f"{pip} install -r requirements.txt", "安装项目依赖"))
|
|
127
|
+
elif "pyproject.toml" in dep_keys or "setup.py" in dep_keys:
|
|
128
|
+
steps.append(_make_step(f"{pip} install -e .", "安装项目(开发模式)"))
|
|
129
|
+
return {
|
|
130
|
+
"project_name": f"{owner}/{repo}",
|
|
131
|
+
"steps": steps,
|
|
132
|
+
"launch_command": f"{_python_cmd(env)} app.py",
|
|
133
|
+
"notes": "GPU 类型已自动识别,如有问题请检查 CUDA/ROCm 驱动版本",
|
|
134
|
+
"confidence": "medium",
|
|
135
|
+
"strategy": "type_template_python_ml",
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
def _plan_python(self, owner, repo, env, dep_keys):
|
|
139
|
+
"""普通 Python 项目"""
|
|
140
|
+
pip = _pip_cmd(env)
|
|
141
|
+
py = _python_cmd(env)
|
|
142
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
143
|
+
_make_step(f"{py} -m venv venv", "创建虚拟环境"),
|
|
144
|
+
_make_step(_venv_activate(env), "激活虚拟环境"),
|
|
145
|
+
]
|
|
146
|
+
if "requirements.txt" in dep_keys:
|
|
147
|
+
steps.append(_make_step(f"{pip} install -r requirements.txt", "安装依赖"))
|
|
148
|
+
elif "pyproject.toml" in dep_keys:
|
|
149
|
+
steps.append(_make_step(f"{pip} install -e '.[all]'", "安装项目(含可选依赖)"))
|
|
150
|
+
elif "setup.py" in dep_keys:
|
|
151
|
+
steps.append(_make_step(f"{pip} install -e .", "安装项目"))
|
|
152
|
+
else:
|
|
153
|
+
steps.append(_make_step(f"{pip} install -r requirements.txt", "安装依赖(尝试)"))
|
|
154
|
+
return {
|
|
155
|
+
"project_name": f"{owner}/{repo}",
|
|
156
|
+
"steps": steps,
|
|
157
|
+
"launch_command": f"{py} main.py",
|
|
158
|
+
"notes": "如有具体启动命令,请查阅项目 README",
|
|
159
|
+
"confidence": "medium",
|
|
160
|
+
"strategy": "type_template_python",
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
def _plan_python_rust_package(self, owner, repo, env):
|
|
164
|
+
"""PyO3/maturin 混合项目,优先按 Python 包安装。"""
|
|
165
|
+
pip = _pip_cmd(env)
|
|
166
|
+
py = _python_cmd(env)
|
|
167
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
168
|
+
_make_step(f"{py} -m venv venv", "创建虚拟环境"),
|
|
169
|
+
_make_step(_venv_activate(env), "激活虚拟环境"),
|
|
170
|
+
_make_step(f"{pip} install -e .", "安装 Python 包(自动触发 Rust 扩展构建)"),
|
|
171
|
+
]
|
|
172
|
+
return {
|
|
173
|
+
"project_name": f"{owner}/{repo}",
|
|
174
|
+
"steps": steps,
|
|
175
|
+
"launch_command": py,
|
|
176
|
+
"notes": "该仓库是 Rust 驱动的 Python 包,优先使用 pip/pyproject 安装而不是 cargo install。",
|
|
177
|
+
"confidence": "medium",
|
|
178
|
+
"strategy": "type_template_python_rust",
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
def _plan_conda(self, owner, repo, env):
|
|
182
|
+
"""含 environment.yml 的 Conda 项目"""
|
|
183
|
+
env_name = repo.lower().replace("-", "_")
|
|
184
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
185
|
+
_make_step(f"conda env create -f environment.yml -n {env_name}", "创建 Conda 环境"),
|
|
186
|
+
_make_step(f"conda activate {env_name}", "激活 Conda 环境"),
|
|
187
|
+
]
|
|
188
|
+
return {
|
|
189
|
+
"project_name": f"{owner}/{repo}",
|
|
190
|
+
"steps": steps,
|
|
191
|
+
"launch_command": f"{_python_cmd(env)} main.py",
|
|
192
|
+
"notes": f"每次使用前需运行:conda activate {env_name}",
|
|
193
|
+
"confidence": "medium",
|
|
194
|
+
"strategy": "type_template_conda",
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
def _plan_node(self, owner, repo, env):
|
|
198
|
+
"""Node.js / TypeScript 项目"""
|
|
199
|
+
install, start = _node_pm(env)
|
|
200
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
201
|
+
_make_step(f"test -f package.json && {install} || echo 'INFO: 未找到 package.json,跳过依赖安装'",
|
|
202
|
+
"安装依赖(如有 package.json)"),
|
|
203
|
+
]
|
|
204
|
+
# 如果有 .env.example,提醒用户创建配置
|
|
205
|
+
steps.append(_make_step("cp .env.example .env 2>/dev/null || echo '无.env模板,跳过'",
|
|
206
|
+
"创建配置文件(如存在模板)"))
|
|
207
|
+
return {
|
|
208
|
+
"project_name": f"{owner}/{repo}",
|
|
209
|
+
"steps": steps,
|
|
210
|
+
"launch_command": start,
|
|
211
|
+
"notes": "如需配置 API Key,编辑 .env 文件",
|
|
212
|
+
"confidence": "medium",
|
|
213
|
+
"strategy": "type_template_node",
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
def _plan_docker(self, owner, repo, env):
|
|
217
|
+
"""Docker 项目"""
|
|
218
|
+
steps = []
|
|
219
|
+
|
|
220
|
+
# 如果没有安装 Docker,先安装
|
|
221
|
+
if not _has_runtime(env, "docker"):
|
|
222
|
+
os_t = _os_type(env)
|
|
223
|
+
if os_t == "macos":
|
|
224
|
+
steps.append(_make_step("brew install --cask docker", "安装 Docker Desktop(macOS)"))
|
|
225
|
+
elif os_t == "linux":
|
|
226
|
+
steps.append(_make_step("curl -fsSL https://get.docker.com | sh",
|
|
227
|
+
"安装 Docker(Linux)", warn=True))
|
|
228
|
+
elif os_t == "windows":
|
|
229
|
+
steps.append(_make_step("winget install Docker.DockerDesktop",
|
|
230
|
+
"安装 Docker Desktop(Windows)"))
|
|
231
|
+
|
|
232
|
+
steps += self._clone_and_cd(owner, repo) + [
|
|
233
|
+
_make_step("docker compose up -d", "后台启动服务"),
|
|
234
|
+
]
|
|
235
|
+
return {
|
|
236
|
+
"project_name": f"{owner}/{repo}",
|
|
237
|
+
"steps": steps,
|
|
238
|
+
"launch_command": "docker compose ps",
|
|
239
|
+
"notes": "查看日志:docker compose logs -f\n停止服务:docker compose down",
|
|
240
|
+
"confidence": "medium",
|
|
241
|
+
"strategy": "type_template_docker",
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
def _plan_rust(self, owner, repo, env):
|
|
245
|
+
"""Rust / Cargo 项目"""
|
|
246
|
+
steps = []
|
|
247
|
+
os_t = _os_type(env)
|
|
248
|
+
if not _has_runtime(env, "rustc"):
|
|
249
|
+
# 不使用 curl|sh(会被安全过滤器拦截),改用包管理器
|
|
250
|
+
if os_t == "macos":
|
|
251
|
+
steps.append(_make_step("brew install rust", "安装 Rust 工具链(Homebrew)"))
|
|
252
|
+
elif os_t == "linux":
|
|
253
|
+
steps.append(_make_step(
|
|
254
|
+
"sudo apt-get install -y cargo rustc",
|
|
255
|
+
"安装 Rust 工具链(apt)"
|
|
256
|
+
))
|
|
257
|
+
elif os_t == "windows":
|
|
258
|
+
steps.append(_make_step("winget install Rustlang.Rust.MSVC", "安装 Rust 工具链"))
|
|
259
|
+
steps.append(_make_step(
|
|
260
|
+
f"cargo install --git {self._get_clone_url(owner, repo).removesuffix('.git')}",
|
|
261
|
+
f"编译安装 {repo}(Cargo,约5-15分钟)"
|
|
262
|
+
))
|
|
263
|
+
return {
|
|
264
|
+
"project_name": f"{owner}/{repo}",
|
|
265
|
+
"steps": steps,
|
|
266
|
+
"launch_command": repo.lower(),
|
|
267
|
+
"notes": "Cargo 编译时间较长,请耐心等待",
|
|
268
|
+
"confidence": "medium",
|
|
269
|
+
"strategy": "type_template_rust",
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
def _plan_go(self, owner, repo, env, dep_keys=frozenset()):
|
|
273
|
+
"""Go 项目"""
|
|
274
|
+
steps = []
|
|
275
|
+
if not _has_runtime(env, "go"):
|
|
276
|
+
os_t = _os_type(env)
|
|
277
|
+
if os_t == "macos":
|
|
278
|
+
steps.append(_make_step("brew install go", "安装 Go"))
|
|
279
|
+
elif os_t == "linux":
|
|
280
|
+
steps.append(_make_step("sudo apt install -y golang-go", "安装 Go"))
|
|
281
|
+
elif os_t == "windows":
|
|
282
|
+
steps.append(_make_step("winget install GoLang.Go", "安装 Go"))
|
|
283
|
+
steps.extend(self._clone_and_cd(owner, repo))
|
|
284
|
+
if "go.mod" in dep_keys:
|
|
285
|
+
# 标准单模块 Go 项目
|
|
286
|
+
steps.append(_make_step("go build ./...", f"编译 {repo}(兼容 CLI 与库项目)"))
|
|
287
|
+
else:
|
|
288
|
+
# 无根 go.mod — 可能是 monorepo 或旧 GOPATH 项目
|
|
289
|
+
steps.append(_make_step(
|
|
290
|
+
"test -f go.mod && go build ./... || "
|
|
291
|
+
"find . -name go.mod -maxdepth 3 -not -path '*/vendor/*' "
|
|
292
|
+
"-exec dirname {} \\; | head -5 | "
|
|
293
|
+
"while read d; do echo \"Building $d\"; (cd \"$d\" && go build ./...); done",
|
|
294
|
+
f"编译 {repo}(自动查找 Go 模块)"
|
|
295
|
+
))
|
|
296
|
+
return {
|
|
297
|
+
"project_name": f"{owner}/{repo}",
|
|
298
|
+
"steps": steps,
|
|
299
|
+
"launch_command": "echo '构建完成,请参考 README 运行具体入口'",
|
|
300
|
+
"notes": "Go 仓库可能是库项目而非 CLI,默认用 go build ./... 做通用验证。",
|
|
301
|
+
"confidence": "medium",
|
|
302
|
+
"strategy": "type_template_go",
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
def _plan_cmake(self, owner, repo, env):
|
|
306
|
+
"""CMake / C++ 项目"""
|
|
307
|
+
os_t = _os_type(env)
|
|
308
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
309
|
+
_make_step("mkdir build && cd build", "创建构建目录"),
|
|
310
|
+
]
|
|
311
|
+
if os_t == "macos":
|
|
312
|
+
steps.append(_make_step(
|
|
313
|
+
"cmake .. && make -j$(sysctl -n hw.ncpu)",
|
|
314
|
+
"编译(利用所有 CPU 核心)"
|
|
315
|
+
))
|
|
316
|
+
elif os_t == "windows":
|
|
317
|
+
steps.append(_make_step(
|
|
318
|
+
"cmake .. && cmake --build . --config Release",
|
|
319
|
+
"编译(Windows)"
|
|
320
|
+
))
|
|
321
|
+
else:
|
|
322
|
+
steps.append(_make_step(
|
|
323
|
+
"cmake .. && make -j$(nproc)",
|
|
324
|
+
"编译(利用所有 CPU 核心)"
|
|
325
|
+
))
|
|
326
|
+
return {
|
|
327
|
+
"project_name": f"{owner}/{repo}",
|
|
328
|
+
"steps": steps,
|
|
329
|
+
"launch_command": f"./build/{repo}",
|
|
330
|
+
"notes": "需要安装 cmake 和 C++ 编译器(Xcode CLT / build-essential)",
|
|
331
|
+
"confidence": "medium",
|
|
332
|
+
"strategy": "type_template_cmake",
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
def _plan_java(self, owner, repo, env, dep_keys, readme):
|
|
336
|
+
"""Java 项目(Maven / Gradle 自适应)"""
|
|
337
|
+
os_t = _os_type(env)
|
|
338
|
+
java_ver = _preferred_java_version(readme)
|
|
339
|
+
steps = self._clone_and_cd(owner, repo)
|
|
340
|
+
# 检测 JDK
|
|
341
|
+
if not _has_runtime(env, "java"):
|
|
342
|
+
if os_t == "macos":
|
|
343
|
+
steps.append(_make_step(f"brew install openjdk@{java_ver}", f"安装 JDK {java_ver}"))
|
|
344
|
+
elif os_t == "linux":
|
|
345
|
+
steps.append(_make_step(f"sudo apt install -y openjdk-{java_ver}-jdk", f"安装 JDK {java_ver}"))
|
|
346
|
+
elif os_t == "windows":
|
|
347
|
+
steps.append(_make_step(f"winget install Microsoft.OpenJDK.{java_ver}", f"安装 JDK {java_ver}"))
|
|
348
|
+
maven_build_cmd = (
|
|
349
|
+
'first_pom=$(find . -name pom.xml -print -quit); '
|
|
350
|
+
'if [ -n "$first_pom" ]; then '
|
|
351
|
+
'mvn_dir=$(dirname "$first_pom"); '
|
|
352
|
+
'if [ -f "$mvn_dir/mvnw" ]; then '
|
|
353
|
+
'(cd "$mvn_dir" && chmod +x mvnw && ./mvnw clean package -DskipTests -U -am) || '
|
|
354
|
+
'mvn -f "$first_pom" clean package -DskipTests -U -am; '
|
|
355
|
+
'else '
|
|
356
|
+
'mvn -f "$first_pom" clean package -DskipTests -U -am; '
|
|
357
|
+
'fi; '
|
|
358
|
+
'else echo "INFO: 未找到 pom.xml,跳过 Maven 编译"; fi'
|
|
359
|
+
)
|
|
360
|
+
gradle_build_cmd = (
|
|
361
|
+
'first_gradle=$(find . \\( -name build.gradle -o -name build.gradle.kts \\) -print -quit); '
|
|
362
|
+
'if [ -n "$first_gradle" ]; then '
|
|
363
|
+
'gradle_dir=$(dirname "$first_gradle"); '
|
|
364
|
+
'if [ -f "$gradle_dir/gradlew" ]; then '
|
|
365
|
+
'(cd "$gradle_dir" && chmod +x gradlew && ./gradlew build -x test --no-daemon) || '
|
|
366
|
+
'gradle -p "$gradle_dir" build -x test --no-daemon; '
|
|
367
|
+
'else '
|
|
368
|
+
'gradle -p "$gradle_dir" build -x test --no-daemon; '
|
|
369
|
+
'fi; '
|
|
370
|
+
'else echo "INFO: 未找到 build.gradle,跳过 Gradle 编译"; fi'
|
|
371
|
+
)
|
|
372
|
+
# Maven vs Gradle
|
|
373
|
+
if "pom.xml" in dep_keys:
|
|
374
|
+
steps.append(_make_step(maven_build_cmd, "Maven 编译打包(跳过测试)"))
|
|
375
|
+
launch = f"java -jar target/{repo}-*.jar"
|
|
376
|
+
elif "build.gradle" in dep_keys or "build.gradle.kts" in dep_keys or "settings.gradle" in dep_keys or "settings.gradle.kts" in dep_keys:
|
|
377
|
+
steps.append(_make_step(gradle_build_cmd, "Gradle 编译打包(跳过测试)"))
|
|
378
|
+
launch = f"./gradlew bootRun || java -jar build/libs/{repo}-*.jar"
|
|
379
|
+
else:
|
|
380
|
+
steps.append(_make_step(
|
|
381
|
+
"find . -name pom.xml -print -quit | grep -q . && ("
|
|
382
|
+
+ maven_build_cmd + ") || "
|
|
383
|
+
"find . \\( -name build.gradle -o -name build.gradle.kts \\) -print -quit | grep -q . && ("
|
|
384
|
+
+ gradle_build_cmd + ") || "
|
|
385
|
+
"echo 'INFO: 未找到构建文件,跳过编译'",
|
|
386
|
+
"检测并编译(自适应 Maven/Gradle)"))
|
|
387
|
+
launch = f"java -jar target/{repo}-*.jar 2>/dev/null || java -jar build/libs/{repo}-*.jar 2>/dev/null || echo '请参考 README 运行'"
|
|
388
|
+
return {
|
|
389
|
+
"project_name": f"{owner}/{repo}",
|
|
390
|
+
"steps": steps,
|
|
391
|
+
"launch_command": launch,
|
|
392
|
+
"notes": f"Java 项目首次编译需要下载依赖,耗时较长。README 若声明 JDK 版本,将优先使用 JDK {java_ver}。",
|
|
393
|
+
"confidence": "medium",
|
|
394
|
+
"strategy": "type_template_java",
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
def _plan_make(self, owner, repo, env, dep_keys):
|
|
398
|
+
"""Makefile / autotools 项目"""
|
|
399
|
+
os_t = _os_type(env)
|
|
400
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
401
|
+
# autotools:有 configure 文件
|
|
402
|
+
if "configure" in dep_keys:
|
|
403
|
+
steps.append(_make_step("./configure", "配置编译选项"))
|
|
404
|
+
# 编译
|
|
405
|
+
if os_t == "macos":
|
|
406
|
+
steps.append(_make_step("make -j$(sysctl -n hw.ncpu)", "编译"))
|
|
407
|
+
else:
|
|
408
|
+
steps.append(_make_step("make -j$(nproc)", "编译"))
|
|
409
|
+
steps.append(_make_step("sudo make install", "安装到系统路径", warn=True))
|
|
410
|
+
return {
|
|
411
|
+
"project_name": f"{owner}/{repo}",
|
|
412
|
+
"steps": steps,
|
|
413
|
+
"launch_command": repo.lower(),
|
|
414
|
+
"notes": "需要 C/C++ 编译器(macOS: Xcode CLT, Linux: build-essential)",
|
|
415
|
+
"confidence": "medium",
|
|
416
|
+
"strategy": "type_template_make",
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
def _plan_ruby(self, owner, repo, env):
|
|
420
|
+
"""Ruby 项目(Bundler)"""
|
|
421
|
+
os_t = _os_type(env)
|
|
422
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
423
|
+
if not _has_runtime(env, "ruby"):
|
|
424
|
+
if os_t == "macos":
|
|
425
|
+
steps.append(_make_step("brew install ruby", "安装 Ruby"))
|
|
426
|
+
elif os_t == "linux":
|
|
427
|
+
steps.append(_make_step("sudo apt install -y ruby-full", "安装 Ruby"))
|
|
428
|
+
steps.append(_make_step("bundle install", "安装 Ruby 依赖"))
|
|
429
|
+
return {
|
|
430
|
+
"project_name": f"{owner}/{repo}",
|
|
431
|
+
"steps": steps,
|
|
432
|
+
"launch_command": f"bundle exec ruby {repo}.rb || bundle exec rails server",
|
|
433
|
+
"notes": "Ruby on Rails 项目请先运行 rails db:migrate",
|
|
434
|
+
"confidence": "medium",
|
|
435
|
+
"strategy": "type_template_ruby",
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
def _plan_php(self, owner, repo, env):
|
|
439
|
+
"""PHP 项目(Composer)"""
|
|
440
|
+
os_t = _os_type(env)
|
|
441
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
442
|
+
if os_t == "macos":
|
|
443
|
+
steps.append(_make_step("brew install php composer", "安装 PHP + Composer(如未安装)"))
|
|
444
|
+
elif os_t == "linux":
|
|
445
|
+
steps.append(_make_step(
|
|
446
|
+
"sudo apt install -y php php-cli php-mbstring php-xml composer",
|
|
447
|
+
"安装 PHP + Composer"))
|
|
448
|
+
steps.append(_make_step("composer install", "安装 PHP 依赖"))
|
|
449
|
+
steps.append(_make_step("cp .env.example .env 2>/dev/null || true", "创建配置文件"))
|
|
450
|
+
return {
|
|
451
|
+
"project_name": f"{owner}/{repo}",
|
|
452
|
+
"steps": steps,
|
|
453
|
+
"launch_command": "php artisan serve || php -S localhost:8000",
|
|
454
|
+
"notes": "Laravel 项目需要:php artisan key:generate",
|
|
455
|
+
"confidence": "medium",
|
|
456
|
+
"strategy": "type_template_php",
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
def _plan_dotnet(self, owner, repo, env):
|
|
460
|
+
""".NET / C# 项目"""
|
|
461
|
+
os_t = _os_type(env)
|
|
462
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
463
|
+
if os_t == "macos":
|
|
464
|
+
steps.append(_make_step("brew install dotnet", "安装 .NET SDK"))
|
|
465
|
+
elif os_t == "linux":
|
|
466
|
+
steps.append(_make_step(
|
|
467
|
+
"sudo apt install -y dotnet-sdk-8.0",
|
|
468
|
+
"安装 .NET SDK 8.0"))
|
|
469
|
+
steps.append(_make_step("dotnet restore", "恢复 NuGet 依赖"))
|
|
470
|
+
steps.append(_make_step("dotnet build", "编译项目"))
|
|
471
|
+
return {
|
|
472
|
+
"project_name": f"{owner}/{repo}",
|
|
473
|
+
"steps": steps,
|
|
474
|
+
"launch_command": "dotnet run",
|
|
475
|
+
"notes": "如有多个项目,用 dotnet run --project src/项目名",
|
|
476
|
+
"confidence": "medium",
|
|
477
|
+
"strategy": "type_template_dotnet",
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
def _plan_swift(self, owner, repo, env):
|
|
481
|
+
"""Swift 项目(Swift Package Manager)"""
|
|
482
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
483
|
+
_make_step("swift build", "编译项目"),
|
|
484
|
+
]
|
|
485
|
+
return {
|
|
486
|
+
"project_name": f"{owner}/{repo}",
|
|
487
|
+
"steps": steps,
|
|
488
|
+
"launch_command": f".build/debug/{repo}",
|
|
489
|
+
"notes": "需要 Xcode 或 Swift 工具链",
|
|
490
|
+
"confidence": "medium",
|
|
491
|
+
"strategy": "type_template_swift",
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
def _plan_kotlin(self, owner, repo, env):
|
|
495
|
+
"""Kotlin 项目(Gradle)"""
|
|
496
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
497
|
+
_make_step("./gradlew build || gradle build", "编译项目"),
|
|
498
|
+
]
|
|
499
|
+
return {
|
|
500
|
+
"project_name": f"{owner}/{repo}",
|
|
501
|
+
"steps": steps,
|
|
502
|
+
"launch_command": "./gradlew run",
|
|
503
|
+
"notes": "需要 JDK 11+。Android 项目请用 Android Studio 打开。",
|
|
504
|
+
"confidence": "medium",
|
|
505
|
+
"strategy": "type_template_kotlin",
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
def _plan_scala(self, owner, repo, env):
|
|
509
|
+
"""Scala 项目(SBT)"""
|
|
510
|
+
os_t = _os_type(env)
|
|
511
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
512
|
+
if os_t == "macos":
|
|
513
|
+
steps.append(_make_step("brew install sbt", "安装 SBT(如未安装)"))
|
|
514
|
+
elif os_t == "linux":
|
|
515
|
+
steps.append(_make_step(
|
|
516
|
+
"echo 'deb https://repo.scala-sbt.org/scalasbt/debian all main' | sudo tee /etc/apt/sources.list.d/sbt.list && sudo apt update && sudo apt install -y sbt",
|
|
517
|
+
"安装 SBT"))
|
|
518
|
+
steps.append(_make_step("sbt compile", "编译项目"))
|
|
519
|
+
return {
|
|
520
|
+
"project_name": f"{owner}/{repo}",
|
|
521
|
+
"steps": steps,
|
|
522
|
+
"launch_command": "sbt run",
|
|
523
|
+
"notes": "需要 JDK 8+。首次编译会下载依赖,耗时较长。",
|
|
524
|
+
"confidence": "medium",
|
|
525
|
+
"strategy": "type_template_scala",
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
def _plan_dart(self, owner, repo, env):
|
|
529
|
+
"""Dart / Flutter 项目"""
|
|
530
|
+
os_t = _os_type(env)
|
|
531
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
532
|
+
if os_t == "macos":
|
|
533
|
+
steps.append(_make_step("brew install flutter || brew install dart", "安装 Flutter / Dart"))
|
|
534
|
+
steps.append(_make_step("dart pub get || flutter pub get", "安装依赖"))
|
|
535
|
+
return {
|
|
536
|
+
"project_name": f"{owner}/{repo}",
|
|
537
|
+
"steps": steps,
|
|
538
|
+
"launch_command": "dart run || flutter run",
|
|
539
|
+
"notes": "Flutter 项目需要设备/模拟器。纯 Dart 项目可直接 dart run。",
|
|
540
|
+
"confidence": "medium",
|
|
541
|
+
"strategy": "type_template_dart",
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
def _plan_elixir(self, owner, repo, env):
|
|
545
|
+
"""Elixir 项目(Mix)"""
|
|
546
|
+
os_t = _os_type(env)
|
|
547
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
548
|
+
if os_t == "macos":
|
|
549
|
+
steps.append(_make_step("brew install elixir", "安装 Elixir"))
|
|
550
|
+
elif os_t == "linux":
|
|
551
|
+
steps.append(_make_step("sudo apt install -y elixir", "安装 Elixir"))
|
|
552
|
+
steps.append(_make_step("mix deps.get", "安装依赖"))
|
|
553
|
+
steps.append(_make_step("mix compile", "编译项目"))
|
|
554
|
+
return {
|
|
555
|
+
"project_name": f"{owner}/{repo}",
|
|
556
|
+
"steps": steps,
|
|
557
|
+
"launch_command": "mix run || iex -S mix",
|
|
558
|
+
"notes": "Phoenix 框架项目请用 mix phx.server",
|
|
559
|
+
"confidence": "medium",
|
|
560
|
+
"strategy": "type_template_elixir",
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
def _plan_haskell(self, owner, repo, env, dependency_files):
|
|
564
|
+
"""Haskell 项目(Stack / Cabal)"""
|
|
565
|
+
os_t = _os_type(env)
|
|
566
|
+
ghcup_cmd = "ghcup" if os_t == "macos" else "$HOME/.ghcup/bin/ghcup"
|
|
567
|
+
has_cabal = _has_haskell_cabal_file(dependency_files)
|
|
568
|
+
resolver = _stack_resolver(dependency_files)
|
|
569
|
+
old_stack_resolver = bool(resolver) and _stack_lts_major(resolver) and _stack_lts_major(resolver) < 20
|
|
570
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
571
|
+
if os_t == "macos":
|
|
572
|
+
steps.append(_make_step(
|
|
573
|
+
"brew install ghcup && ghcup install ghc recommended && ghcup install cabal recommended && ghcup install stack latest",
|
|
574
|
+
"安装 GHCup + GHC + Cabal + Stack"))
|
|
575
|
+
elif os_t == "linux":
|
|
576
|
+
steps.append(_make_step(
|
|
577
|
+
"curl --proto '=https' --tlsv1.2 -sSf https://get-ghcup.haskell.org | sh && $HOME/.ghcup/bin/ghcup install ghc recommended && $HOME/.ghcup/bin/ghcup install cabal recommended && $HOME/.ghcup/bin/ghcup install stack latest",
|
|
578
|
+
"安装 GHCup(Haskell 工具链管理)", warn=True))
|
|
579
|
+
system_packages = _haskell_system_packages(dependency_files, env)
|
|
580
|
+
env_prefix = _haskell_macos_env_prefix(dependency_files, env)
|
|
581
|
+
if system_packages:
|
|
582
|
+
if os_t == "macos":
|
|
583
|
+
steps.append(_make_step(f"brew install {' '.join(system_packages)}", "安装 Haskell 常见系统库依赖"))
|
|
584
|
+
elif os_t == "linux":
|
|
585
|
+
steps.append(_make_step(f"sudo apt-get install -y {' '.join(system_packages)}", "安装 Haskell 常见系统库依赖"))
|
|
586
|
+
repo_template = _haskell_repo_template(owner, repo, ghcup_cmd, env_prefix)
|
|
587
|
+
template_notes: list[str] = []
|
|
588
|
+
if repo_template is not None:
|
|
589
|
+
build_cmd, launch_cmd, template_notes = repo_template
|
|
590
|
+
if has_cabal:
|
|
591
|
+
if repo_template is None:
|
|
592
|
+
build_cmd = (
|
|
593
|
+
f"{env_prefix} ({ghcup_cmd} run --ghc recommended --cabal recommended -- cabal update && "
|
|
594
|
+
f"{ghcup_cmd} run --ghc recommended --cabal recommended -- cabal build all)"
|
|
595
|
+
f" || {ghcup_cmd} run --stack latest -- stack build"
|
|
596
|
+
)
|
|
597
|
+
launch_cmd = (
|
|
598
|
+
f"{env_prefix} {ghcup_cmd} run --ghc recommended --cabal recommended -- cabal run"
|
|
599
|
+
f" || {ghcup_cmd} run --stack latest -- stack run"
|
|
600
|
+
)
|
|
601
|
+
else:
|
|
602
|
+
if repo_template is None:
|
|
603
|
+
build_cmd = f"{env_prefix} {ghcup_cmd} run --stack latest -- stack build"
|
|
604
|
+
launch_cmd = f"{env_prefix} {ghcup_cmd} run --stack latest -- stack run"
|
|
605
|
+
steps.append(_make_step(build_cmd, "编译项目"))
|
|
606
|
+
notes = ["首次编译会下载 GHC 和所有依赖,耗时可能较长。"]
|
|
607
|
+
if resolver:
|
|
608
|
+
notes.append(f"检测到 stack resolver: {resolver}。")
|
|
609
|
+
if has_cabal:
|
|
610
|
+
notes.append("优先走 ghcup run + cabal,避免 cabal/ghc PATH 不一致导致的假失败。")
|
|
611
|
+
if system_packages:
|
|
612
|
+
notes.append(f"已为 Haskell 预检常见系统库依赖: {', '.join(system_packages)}。")
|
|
613
|
+
if env_prefix:
|
|
614
|
+
notes.append("macOS 上会自动导出 Homebrew 的 pkg-config、include、lib 路径,避免 PCRE/GTK 头文件找不到。")
|
|
615
|
+
if old_stack_resolver and _is_apple_silicon(env):
|
|
616
|
+
notes.append("旧版 Stack resolver 在 Apple Silicon 上成功率较低;若 stack 失败,优先接受 cabal 路径结果。")
|
|
617
|
+
notes.extend(template_notes)
|
|
618
|
+
return {
|
|
619
|
+
"project_name": f"{owner}/{repo}",
|
|
620
|
+
"steps": steps,
|
|
621
|
+
"launch_command": launch_cmd,
|
|
622
|
+
"notes": " ".join(notes),
|
|
623
|
+
"confidence": "medium",
|
|
624
|
+
"strategy": "type_template_haskell",
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
def _plan_lua(self, owner, repo, env):
|
|
628
|
+
"""Lua 项目"""
|
|
629
|
+
os_t = _os_type(env)
|
|
630
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
631
|
+
if os_t == "macos":
|
|
632
|
+
steps.append(_make_step("brew install lua luarocks", "安装 Lua + LuaRocks"))
|
|
633
|
+
elif os_t == "linux":
|
|
634
|
+
steps.append(_make_step("sudo apt install -y lua5.4 luarocks", "安装 Lua"))
|
|
635
|
+
return {
|
|
636
|
+
"project_name": f"{owner}/{repo}",
|
|
637
|
+
"steps": steps,
|
|
638
|
+
"launch_command": f"lua init.lua || lua {repo}.lua",
|
|
639
|
+
"notes": "Neovim 插件项目无需编译,把目录放入 Neovim 插件路径即可。",
|
|
640
|
+
"confidence": "medium",
|
|
641
|
+
"strategy": "type_template_lua",
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
def _plan_perl(self, owner, repo, env):
|
|
645
|
+
"""Perl 项目"""
|
|
646
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
647
|
+
_make_step("cpanm --installdeps . || perl Makefile.PL && make && make install", "安装依赖"),
|
|
648
|
+
]
|
|
649
|
+
return {
|
|
650
|
+
"project_name": f"{owner}/{repo}",
|
|
651
|
+
"steps": steps,
|
|
652
|
+
"launch_command": f"perl {repo}.pl",
|
|
653
|
+
"notes": "推荐使用 cpanminus (cpanm) 管理依赖。",
|
|
654
|
+
"confidence": "medium",
|
|
655
|
+
"strategy": "type_template_perl",
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
def _plan_r(self, owner, repo, env):
|
|
659
|
+
"""R 语言项目"""
|
|
660
|
+
os_t = _os_type(env)
|
|
661
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
662
|
+
if os_t == "macos":
|
|
663
|
+
steps.append(_make_step("brew install r", "安装 R"))
|
|
664
|
+
elif os_t == "linux":
|
|
665
|
+
steps.append(_make_step("sudo apt install -y r-base r-base-dev", "安装 R"))
|
|
666
|
+
steps.append(_make_step("Rscript -e 'if(file.exists(\"DESCRIPTION\")) devtools::install_deps()'", "安装依赖"))
|
|
667
|
+
return {
|
|
668
|
+
"project_name": f"{owner}/{repo}",
|
|
669
|
+
"steps": steps,
|
|
670
|
+
"launch_command": "Rscript main.R || R CMD BATCH main.R",
|
|
671
|
+
"notes": "R 包项目可用 R CMD INSTALL . 安装到本地 R 库。",
|
|
672
|
+
"confidence": "medium",
|
|
673
|
+
"strategy": "type_template_r",
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
def _plan_julia(self, owner, repo, env):
|
|
677
|
+
"""Julia 项目"""
|
|
678
|
+
os_t = _os_type(env)
|
|
679
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
680
|
+
if os_t == "macos":
|
|
681
|
+
steps.append(_make_step("brew install julia", "安装 Julia"))
|
|
682
|
+
elif os_t == "linux":
|
|
683
|
+
steps.append(_make_step(
|
|
684
|
+
"curl -fsSL https://install.julialang.org | sh",
|
|
685
|
+
"安装 Julia", warn=True))
|
|
686
|
+
steps.append(_make_step(
|
|
687
|
+
"julia --project=. -e 'using Pkg; Pkg.instantiate()'",
|
|
688
|
+
"安装 Julia 依赖"))
|
|
689
|
+
return {
|
|
690
|
+
"project_name": f"{owner}/{repo}",
|
|
691
|
+
"steps": steps,
|
|
692
|
+
"launch_command": "julia --project=. src/main.jl",
|
|
693
|
+
"notes": "Julia 首次运行会预编译依赖,耗时较长。",
|
|
694
|
+
"confidence": "medium",
|
|
695
|
+
"strategy": "type_template_julia",
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
def _plan_zig(self, owner, repo, env, dependency_files):
|
|
699
|
+
"""Zig 项目"""
|
|
700
|
+
os_t = _os_type(env)
|
|
701
|
+
min_version = _zig_minimum_version(dependency_files)
|
|
702
|
+
legacy_build_api = _zig_uses_legacy_build_api(dependency_files)
|
|
703
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
704
|
+
if os_t == "macos":
|
|
705
|
+
steps.append(_make_step("brew install zig", "安装 Zig"))
|
|
706
|
+
elif os_t == "linux":
|
|
707
|
+
steps.append(_make_step("snap install zig --classic", "安装 Zig"))
|
|
708
|
+
build_cmd = "zig build"
|
|
709
|
+
notes = ["Zig 也可用于编译 C/C++ 项目。"]
|
|
710
|
+
if os_t == "macos":
|
|
711
|
+
build_cmd = (
|
|
712
|
+
'if [ -d /Applications/Xcode.app/Contents/Developer ]; then '
|
|
713
|
+
'export DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer; '
|
|
714
|
+
'fi; export SDKROOT="${SDKROOT:-$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)}"; zig build'
|
|
715
|
+
)
|
|
716
|
+
notes.append("macOS 上 Zig 依赖 Apple SDK;若报 DarwinSdkNotFound,应优先检查 Xcode/Command Line Tools,而不是重复重试。")
|
|
717
|
+
if min_version:
|
|
718
|
+
notes.append(f"项目声明最低 Zig 版本: {min_version}。")
|
|
719
|
+
if legacy_build_api:
|
|
720
|
+
notes.append("检测到旧版 Zig build API(如 root_source_file/source_file);若当前 Zig 0.15+ 失败,应优先判定为版本兼容问题,而不是继续盲目重试。")
|
|
721
|
+
steps.append(_make_step(build_cmd, "编译项目"))
|
|
722
|
+
return {
|
|
723
|
+
"project_name": f"{owner}/{repo}",
|
|
724
|
+
"steps": steps,
|
|
725
|
+
"launch_command": "./zig-out/bin/" + repo,
|
|
726
|
+
"notes": " ".join(notes),
|
|
727
|
+
"confidence": "medium",
|
|
728
|
+
"strategy": "type_template_zig",
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
def _plan_clojure(self, owner, repo, env):
|
|
732
|
+
"""Clojure 项目(Leiningen)"""
|
|
733
|
+
os_t = _os_type(env)
|
|
734
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
735
|
+
if os_t == "macos":
|
|
736
|
+
steps.append(_make_step("brew install leiningen", "安装 Leiningen"))
|
|
737
|
+
elif os_t == "linux":
|
|
738
|
+
steps.append(_make_step(
|
|
739
|
+
"curl -fsSL https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein -o /usr/local/bin/lein && chmod +x /usr/local/bin/lein",
|
|
740
|
+
"安装 Leiningen", warn=True))
|
|
741
|
+
steps.append(_make_step("lein deps", "安装 Clojure 依赖"))
|
|
742
|
+
return {
|
|
743
|
+
"project_name": f"{owner}/{repo}",
|
|
744
|
+
"steps": steps,
|
|
745
|
+
"launch_command": "lein run || lein repl",
|
|
746
|
+
"notes": "需要 JDK 8+。lein repl 启动交互式开发环境。",
|
|
747
|
+
"confidence": "medium",
|
|
748
|
+
"strategy": "type_template_clojure",
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
def _plan_meson(self, owner, repo, env):
|
|
752
|
+
"""Meson 构建系统项目"""
|
|
753
|
+
os_t = _os_type(env)
|
|
754
|
+
steps = list(self._clone_and_cd(owner, repo))
|
|
755
|
+
if os_t == "macos":
|
|
756
|
+
steps.append(_make_step("brew install meson ninja", "安装 Meson + Ninja"))
|
|
757
|
+
elif os_t == "linux":
|
|
758
|
+
steps.append(_make_step("sudo apt install -y meson ninja-build", "安装 Meson + Ninja"))
|
|
759
|
+
steps.append(_make_step("meson setup builddir", "配置 Meson 构建目录"))
|
|
760
|
+
steps.append(_make_step("ninja -C builddir", "编译"))
|
|
761
|
+
return {
|
|
762
|
+
"project_name": f"{owner}/{repo}",
|
|
763
|
+
"steps": steps,
|
|
764
|
+
"launch_command": f"./builddir/{repo}",
|
|
765
|
+
"notes": "需要 C/C++ 编译器。meson test -C builddir 运行测试。",
|
|
766
|
+
"confidence": "medium",
|
|
767
|
+
"strategy": "type_template_meson",
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
def _plan_shell(self, owner, repo, env):
|
|
771
|
+
"""Shell 脚本项目"""
|
|
772
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
773
|
+
_make_step("chmod +x *.sh install.sh 2>/dev/null || true", "添加执行权限"),
|
|
774
|
+
]
|
|
775
|
+
return {
|
|
776
|
+
"project_name": f"{owner}/{repo}",
|
|
777
|
+
"steps": steps,
|
|
778
|
+
"launch_command": "bash install.sh || bash setup.sh || bash main.sh",
|
|
779
|
+
"notes": "Shell 项目通常包含 install.sh 或 setup.sh 脚本。请先阅读后再执行。",
|
|
780
|
+
"confidence": "medium",
|
|
781
|
+
"strategy": "type_template_shell",
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
# ─────────────────────────────────────────
|
|
785
|
+
# PlatformIO / Arduino 模板
|
|
786
|
+
# ─────────────────────────────────────────
|
|
787
|
+
|
|
788
|
+
def _plan_platformio(self, owner, repo, env):
|
|
789
|
+
"""PlatformIO / Arduino 嵌入式项目"""
|
|
790
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
791
|
+
_make_step("pip install platformio || brew install platformio", "安装 PlatformIO"),
|
|
792
|
+
_make_step("pio run", "编译项目"),
|
|
793
|
+
]
|
|
794
|
+
return {
|
|
795
|
+
"project_name": f"{owner}/{repo}",
|
|
796
|
+
"steps": steps,
|
|
797
|
+
"launch_command": "pio run -t upload",
|
|
798
|
+
"notes": "PlatformIO 嵌入式项目。pio run 编译,pio run -t upload 上传到板子。\n"
|
|
799
|
+
"如需特定开发板,请查阅 platformio.ini 中的 [env] 配置。",
|
|
800
|
+
"confidence": "medium",
|
|
801
|
+
"strategy": "type_template_platformio",
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
# ─────────────────────────────────────────
|
|
805
|
+
# C/C++ 通用模板(无 CMakeLists / Makefile)
|
|
806
|
+
# ─────────────────────────────────────────
|
|
807
|
+
|
|
808
|
+
def _plan_c_cpp(self, owner, repo, env):
|
|
809
|
+
"""纯 C/C++ 项目的通用保底模板"""
|
|
810
|
+
steps = self._clone_and_cd(owner, repo) + [
|
|
811
|
+
_make_step(
|
|
812
|
+
"if [ -f CMakeLists.txt ]; then mkdir -p build && cd build && cmake .. && make; "
|
|
813
|
+
"elif [ -f Makefile ]; then make; "
|
|
814
|
+
"elif [ -f configure ]; then ./configure && make; "
|
|
815
|
+
"elif [ -f meson.build ]; then meson setup build && ninja -C build; "
|
|
816
|
+
"else echo '未找到构建文件,请查阅 README'; fi",
|
|
817
|
+
"自动检测并构建"
|
|
818
|
+
),
|
|
819
|
+
]
|
|
820
|
+
return {
|
|
821
|
+
"project_name": f"{owner}/{repo}",
|
|
822
|
+
"steps": steps,
|
|
823
|
+
"launch_command": "",
|
|
824
|
+
"notes": "C/C++ 通用模板。自动检测 CMake/Make/configure/meson 构建系统。\n"
|
|
825
|
+
"如需安装系统依赖,请查阅 README。",
|
|
826
|
+
"confidence": "low",
|
|
827
|
+
"strategy": "type_template_c_cpp",
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
# ─────────────────────────────────────────
|
|
831
|
+
# 策略 3:README 提取
|
|
832
|
+
# ─────────────────────────────────────────
|
|
833
|
+
|
|
834
|
+
def _plan_from_readme(self, owner, repo, readme, project_types=None) -> dict:
|
|
835
|
+
"""从 README 代码块中提取安装命令(增强版:section 感知 + 宽泛代码块匹配)"""
|
|
836
|
+
steps = []
|
|
837
|
+
seen: set[str] = set()
|
|
838
|
+
types = set(project_types or [])
|
|
839
|
+
|
|
840
|
+
# ── 提取代码块(匹配任意语言标签) ──
|
|
841
|
+
code_blocks = re.findall(
|
|
842
|
+
r'```[^\n]*\n(.*?)```',
|
|
843
|
+
readme, re.DOTALL | re.IGNORECASE
|
|
844
|
+
)
|
|
845
|
+
# 也提取缩进代码块(4空格或1tab开头的连续行)
|
|
846
|
+
for m in re.finditer(r'(?:^|\n)((?:(?: |\t)[^\n]+\n?)+)', readme):
|
|
847
|
+
block = re.sub(r'^(?: |\t)', '', m.group(1), flags=re.MULTILINE)
|
|
848
|
+
code_blocks.append(block)
|
|
849
|
+
# 提取 $ 命令行(README 中 `$ pip install foo` 样式)
|
|
850
|
+
for m in re.finditer(r'^\s*\$\s+(.+)$', readme, re.MULTILINE):
|
|
851
|
+
code_blocks.append(m.group(1))
|
|
852
|
+
|
|
853
|
+
# ── section 感知:优先从安装相关章节提取 ──
|
|
854
|
+
_INSTALL_HEADINGS = re.compile(
|
|
855
|
+
r'^#{1,3}\s+(?:install|setup|getting\s+started|build|quick\s+start'
|
|
856
|
+
r'|usage|compilation|安装|构建|快速开始)',
|
|
857
|
+
re.MULTILINE | re.IGNORECASE
|
|
858
|
+
)
|
|
859
|
+
priority_blocks = []
|
|
860
|
+
for m in _INSTALL_HEADINGS.finditer(readme):
|
|
861
|
+
# 取该 heading 到下一个同级 heading 之间的内容
|
|
862
|
+
start = m.end()
|
|
863
|
+
next_heading = re.search(r'^#{1,3}\s+', readme[start:], re.MULTILINE)
|
|
864
|
+
section = readme[start:start + next_heading.start()] if next_heading else readme[start:]
|
|
865
|
+
section_code = re.findall(r'```[^\n]*\n(.*?)```', section, re.DOTALL)
|
|
866
|
+
priority_blocks.extend(section_code)
|
|
867
|
+
|
|
868
|
+
# 优先使用安装章节的代码块,然后是全部代码块
|
|
869
|
+
ordered_blocks = priority_blocks + code_blocks
|
|
870
|
+
|
|
871
|
+
# 按优先级的模式(扩展至 30+)
|
|
872
|
+
_PATTERNS = [
|
|
873
|
+
# 克隆 / 下载
|
|
874
|
+
(r'git\s+clone\s+\S+', "克隆代码"),
|
|
875
|
+
(r'curl[^\n]+\|\s*(?:bash|sh)', "下载执行安装脚本"),
|
|
876
|
+
(r'wget\s+\S+', "下载文件"),
|
|
877
|
+
(r'curl\s+(?:-[fsSLOk]+\s+)*\S+', "下载文件"),
|
|
878
|
+
# Python
|
|
879
|
+
(r'pip(?:3)?\s+install\s+-r\s+\S+', "安装 Python 依赖"),
|
|
880
|
+
(r'pip(?:3)?\s+install\s+-e\s+\S[^\n]*', "开发模式安装"),
|
|
881
|
+
(r'pip(?:3)?\s+install\s+\S[^\n]+', "安装 Python 包"),
|
|
882
|
+
(r'python(?:3)?\s+setup\.py\s+\S+', "Python setup.py"),
|
|
883
|
+
(r'conda\s+env\s+create[^\n]+', "创建 Conda 环境"),
|
|
884
|
+
(r'conda\s+install[^\n]+', "Conda 安装"),
|
|
885
|
+
(r'conda\s+activate\s+\S+', "激活 Conda 环境"),
|
|
886
|
+
# Node.js
|
|
887
|
+
(r'npm\s+(?:install|i|ci)[^\n]*', "安装 Node.js 包"),
|
|
888
|
+
(r'pnpm\s+install[^\n]*', "安装 Node.js 包(pnpm)"),
|
|
889
|
+
(r'yarn(?:\s+install)?[^\n]*', "安装 Node.js 包(yarn)"),
|
|
890
|
+
(r'npx\s+\S[^\n]*', "npx 执行"),
|
|
891
|
+
# Docker
|
|
892
|
+
(r'docker\s+(?:run|pull|compose|build)[^\n]+', "Docker 运行"),
|
|
893
|
+
(r'docker-compose\s+(?:up|build)[^\n]*', "Docker Compose"),
|
|
894
|
+
# 系统包管理
|
|
895
|
+
(r'brew\s+install[^\n]+', "Homebrew 安装"),
|
|
896
|
+
(r'(?:sudo\s+)?apt(?:-get)?\s+install[^\n]+', "APT 安装"),
|
|
897
|
+
(r'(?:sudo\s+)?yum\s+install[^\n]+', "YUM 安装"),
|
|
898
|
+
(r'(?:sudo\s+)?pacman\s+-S[^\n]+', "Pacman 安装"),
|
|
899
|
+
# C/C++ 构建
|
|
900
|
+
(r'(?:sudo\s+)?make(?:\s+install)?', "Make 构建"),
|
|
901
|
+
(r'cmake\s+[^\n]+', "CMake 配置"),
|
|
902
|
+
(r'\./configure[^\n]*', "configure 配置"),
|
|
903
|
+
(r'(?:sudo\s+)?make\s+-j\s*\d*[^\n]*', "并行 Make 构建"),
|
|
904
|
+
(r'mkdir\s+(?:-p\s+)?build[^\n]*', "创建构建目录"),
|
|
905
|
+
# PlatformIO / Arduino
|
|
906
|
+
(r'(?:platformio|pio)\s+(?:run|init|lib)[^\n]*', "PlatformIO 构建"),
|
|
907
|
+
(r'arduino-cli\s+\S[^\n]*', "Arduino CLI"),
|
|
908
|
+
# Rust / Go / Cargo
|
|
909
|
+
(r'cargo\s+(?:install|build|run)[^\n]+', "Cargo 操作"),
|
|
910
|
+
(r'go\s+(?:install|build|get)[^\n]+', "Go 操作"),
|
|
911
|
+
# Java / Gradle / Maven
|
|
912
|
+
(r'(?:\./)?gradlew?\s+\S[^\n]*', "Gradle 构建"),
|
|
913
|
+
(r'mvn\s+\S[^\n]*', "Maven 构建"),
|
|
914
|
+
# Ruby / PHP / Other
|
|
915
|
+
(r'bundle\s+install[^\n]*', "Ruby 依赖安装"),
|
|
916
|
+
(r'gem\s+install[^\n]+', "Gem 安装"),
|
|
917
|
+
(r'composer\s+install[^\n]*', "Composer 安装"),
|
|
918
|
+
# 通用
|
|
919
|
+
(r'cd\s+\S+', "切换目录"),
|
|
920
|
+
(r'chmod\s+\+x\s+\S+', "添加执行权限"),
|
|
921
|
+
(r'\./(?:install|setup|build|run)\S*', "执行脚本"),
|
|
922
|
+
]
|
|
923
|
+
|
|
924
|
+
_BLOCK_DANGEROUS = [
|
|
925
|
+
r'rm\s+-rf\s+/', r':\(\)\{', r'mkfs\.', r'dd\s+if=',
|
|
926
|
+
r'format\s+[cCdDeE]:', r'shutdown', r'reboot',
|
|
927
|
+
]
|
|
928
|
+
|
|
929
|
+
for block in ordered_blocks:
|
|
930
|
+
for pattern, desc in _PATTERNS:
|
|
931
|
+
for m in re.finditer(pattern, block, re.IGNORECASE):
|
|
932
|
+
cmd = m.group(0).strip()
|
|
933
|
+
if any(re.search(d, cmd, re.IGNORECASE) for d in _BLOCK_DANGEROUS):
|
|
934
|
+
continue
|
|
935
|
+
if cmd not in seen and len(cmd) > 3:
|
|
936
|
+
seen.add(cmd)
|
|
937
|
+
warn = "⚠️ 执行前请确认命令来源可信" if "| sh" in cmd or "| bash" in cmd else ""
|
|
938
|
+
steps.append({"command": cmd, "description": desc, "_warning": warn})
|
|
939
|
+
|
|
940
|
+
# ── 类型感知兜底:提取失败时根据 project_types 生成通用步骤 ──
|
|
941
|
+
if not steps and types:
|
|
942
|
+
steps.extend(self._clone_and_cd(owner, repo))
|
|
943
|
+
if types & {"c", "cpp", "cmake", "make"}:
|
|
944
|
+
steps.append(_make_step("mkdir -p build && cd build && cmake .. || cd .. && make", "构建项目"))
|
|
945
|
+
elif "python" in types:
|
|
946
|
+
steps.append(_make_step("pip install -e . || pip install -r requirements.txt", "安装依赖"))
|
|
947
|
+
elif "node" in types:
|
|
948
|
+
steps.append(_make_step("npm install", "安装依赖"))
|
|
949
|
+
elif "rust" in types:
|
|
950
|
+
steps.append(_make_step("cargo build --release", "构建项目"))
|
|
951
|
+
elif "go" in types:
|
|
952
|
+
steps.append(_make_step("go build ./...", "构建项目"))
|
|
953
|
+
|
|
954
|
+
confidence = "medium" if len(steps) >= 3 else ("low" if steps else "low")
|
|
955
|
+
message = "" if steps else (
|
|
956
|
+
"⚠️ 未能从 README 中自动提取到安装命令。\n"
|
|
957
|
+
"建议:(1) 手动查阅项目 README\n"
|
|
958
|
+
" (2) 配置任意 LLM(哪怕免费的 Groq)以获得 AI 辅助分析"
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
return {
|
|
962
|
+
"project_name": f"{owner}/{repo}",
|
|
963
|
+
"steps": steps,
|
|
964
|
+
"launch_command": "",
|
|
965
|
+
"notes": message or "规则提取模式,建议对照 README 确认每一步",
|
|
966
|
+
"confidence": confidence,
|
|
967
|
+
"strategy": "readme_extract",
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
# ─────────────────────────────────────────
|
|
971
|
+
# 辅助方法
|
|
972
|
+
# ─────────────────────────────────────────
|
|
973
|
+
|
|
974
|
+
def _resolve_step(self, step: dict, env: dict,
|
|
975
|
+
node_install: str, node_dev: str) -> dict:
|
|
976
|
+
cmd = self._fill(step["cmd"], env, node_install, node_dev)
|
|
977
|
+
warn = "⚠️ 执行前请确认命令来源可信" if step.get("warn") else ""
|
|
978
|
+
return {"command": cmd, "description": step.get("desc", ""), "_warning": warn}
|
|
979
|
+
|
|
980
|
+
def _fill(self, text: str, env: dict, node_install: str, node_dev: str) -> str:
|
|
981
|
+
"""替换模板占位符为平台正确的命令"""
|
|
982
|
+
if not text:
|
|
983
|
+
return text
|
|
984
|
+
replacements = {
|
|
985
|
+
"{python}": _python_cmd(env),
|
|
986
|
+
"{pip}": _pip_cmd(env),
|
|
987
|
+
"{venv_activate}": _venv_activate(env),
|
|
988
|
+
"{torch_install}": _torch_install_cmd(env),
|
|
989
|
+
"{node_install}": node_install,
|
|
990
|
+
"{node_dev}": node_dev,
|
|
991
|
+
}
|
|
992
|
+
for k, v in replacements.items():
|
|
993
|
+
text = text.replace(k, v)
|
|
994
|
+
return text
|
|
995
|
+
|
|
996
|
+
|