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.
Files changed (59) hide show
  1. gitinstall/__init__.py +61 -0
  2. gitinstall/_sdk.py +541 -0
  3. gitinstall/academic.py +831 -0
  4. gitinstall/admin.html +327 -0
  5. gitinstall/auto_update.py +384 -0
  6. gitinstall/autopilot.py +349 -0
  7. gitinstall/badge.py +476 -0
  8. gitinstall/checkpoint.py +330 -0
  9. gitinstall/cicd.py +499 -0
  10. gitinstall/clawhub.html +718 -0
  11. gitinstall/config_schema.py +353 -0
  12. gitinstall/db.py +984 -0
  13. gitinstall/db_backend.py +445 -0
  14. gitinstall/dep_chain.py +337 -0
  15. gitinstall/dependency_audit.py +1153 -0
  16. gitinstall/detector.py +542 -0
  17. gitinstall/doctor.py +493 -0
  18. gitinstall/education.py +869 -0
  19. gitinstall/enterprise.py +802 -0
  20. gitinstall/error_fixer.py +953 -0
  21. gitinstall/event_bus.py +251 -0
  22. gitinstall/executor.py +577 -0
  23. gitinstall/feature_flags.py +138 -0
  24. gitinstall/fetcher.py +921 -0
  25. gitinstall/huggingface.py +922 -0
  26. gitinstall/hw_detect.py +988 -0
  27. gitinstall/i18n.py +664 -0
  28. gitinstall/installer_registry.py +362 -0
  29. gitinstall/knowledge_base.py +379 -0
  30. gitinstall/license_check.py +605 -0
  31. gitinstall/llm.py +569 -0
  32. gitinstall/log.py +236 -0
  33. gitinstall/main.py +1408 -0
  34. gitinstall/mcp_agent.py +841 -0
  35. gitinstall/mcp_server.py +386 -0
  36. gitinstall/monorepo.py +810 -0
  37. gitinstall/multi_source.py +425 -0
  38. gitinstall/onboard.py +276 -0
  39. gitinstall/planner.py +222 -0
  40. gitinstall/planner_helpers.py +323 -0
  41. gitinstall/planner_known_projects.py +1010 -0
  42. gitinstall/planner_templates.py +996 -0
  43. gitinstall/remote_gpu.py +633 -0
  44. gitinstall/resilience.py +608 -0
  45. gitinstall/run_tests.py +572 -0
  46. gitinstall/skills.py +476 -0
  47. gitinstall/tool_schemas.py +324 -0
  48. gitinstall/trending.py +279 -0
  49. gitinstall/uninstaller.py +415 -0
  50. gitinstall/validate_top100.py +607 -0
  51. gitinstall/watchdog.py +180 -0
  52. gitinstall/web.py +1277 -0
  53. gitinstall/web_ui.html +2277 -0
  54. gitinstall-1.1.0.dist-info/METADATA +275 -0
  55. gitinstall-1.1.0.dist-info/RECORD +59 -0
  56. gitinstall-1.1.0.dist-info/WHEEL +5 -0
  57. gitinstall-1.1.0.dist-info/entry_points.txt +3 -0
  58. gitinstall-1.1.0.dist-info/licenses/LICENSE +21 -0
  59. gitinstall-1.1.0.dist-info/top_level.txt +1 -0
gitinstall/log.py ADDED
@@ -0,0 +1,236 @@
1
+ """
2
+ log.py - gitinstall 统一结构化日志模块
3
+ ========================================
4
+
5
+ 企业级日志基础设施,零外部依赖。
6
+ 支持:JSON 结构化输出、日志轮转、分级过滤、上下文注入。
7
+
8
+ 用法:
9
+ from log import get_logger
10
+ logger = get_logger(__name__)
11
+ logger.info("安装开始", project="comfyui", strategy="pip")
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import logging
18
+ import logging.handlers
19
+ import os
20
+ import sys
21
+ import time
22
+ import threading
23
+ from pathlib import Path
24
+ from typing import Any
25
+
26
+ # ── 日志目录 ──
27
+ LOG_DIR = Path.home() / ".gitinstall" / "logs"
28
+
29
+ # ── 全局配置 ──
30
+ _configured = False
31
+ _config_lock = threading.Lock()
32
+
33
+
34
+ class StructuredFormatter(logging.Formatter):
35
+ """结构化日志格式化器,支持 JSON 和人类可读两种模式"""
36
+
37
+ def __init__(self, json_mode: bool = False):
38
+ super().__init__()
39
+ self.json_mode = json_mode
40
+
41
+ def format(self, record: logging.LogRecord) -> str:
42
+ # 提取额外字段(由 StructuredLoggerAdapter 注入)
43
+ extra = getattr(record, "_structured_extra", {})
44
+
45
+ if self.json_mode:
46
+ log_entry = {
47
+ "ts": record.created,
48
+ "level": record.levelname,
49
+ "logger": record.name,
50
+ "msg": record.getMessage(),
51
+ "module": record.module,
52
+ "func": record.funcName,
53
+ "line": record.lineno,
54
+ }
55
+ if extra:
56
+ log_entry["extra"] = extra
57
+ if record.exc_info and record.exc_info[0]:
58
+ log_entry["exception"] = self.formatException(record.exc_info)
59
+ return json.dumps(log_entry, ensure_ascii=False, default=str)
60
+ else:
61
+ ts = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(record.created))
62
+ base = f"{ts} [{record.levelname:>7s}] {record.name}: {record.getMessage()}"
63
+ if extra:
64
+ kv = " ".join(f"{k}={v}" for k, v in extra.items())
65
+ base += f" | {kv}"
66
+ if record.exc_info and record.exc_info[0]:
67
+ base += "\n" + self.formatException(record.exc_info)
68
+ return base
69
+
70
+
71
+ class StructuredLoggerAdapter(logging.LoggerAdapter):
72
+ """支持结构化字段的日志适配器
73
+
74
+ 用法:
75
+ logger.info("消息", project="foo", step=3)
76
+ """
77
+
78
+ def process(self, msg, kwargs):
79
+ # 从 kwargs 中提取非标准字段作为结构化数据
80
+ extra = {}
81
+ standard_keys = {"exc_info", "stack_info", "stacklevel", "extra"}
82
+ for k in list(kwargs.keys()):
83
+ if k not in standard_keys:
84
+ extra[k] = kwargs.pop(k)
85
+
86
+ # 合并到 extra dict
87
+ if "extra" not in kwargs:
88
+ kwargs["extra"] = {}
89
+ kwargs["extra"]["_structured_extra"] = {
90
+ **self.extra,
91
+ **extra,
92
+ }
93
+ return msg, kwargs
94
+
95
+
96
+ def _ensure_log_dir():
97
+ """确保日志目录存在并设置安全权限"""
98
+ LOG_DIR.mkdir(parents=True, exist_ok=True)
99
+ try:
100
+ os.chmod(LOG_DIR, 0o700)
101
+ except OSError:
102
+ pass
103
+
104
+
105
+ def configure(
106
+ level: str = None,
107
+ json_mode: bool = None,
108
+ log_file: bool = None,
109
+ max_bytes: int = 10 * 1024 * 1024, # 10MB
110
+ backup_count: int = 5,
111
+ ):
112
+ """
113
+ 配置全局日志系统。只在首次调用时生效。
114
+
115
+ 环境变量覆盖:
116
+ GITINSTALL_LOG_LEVEL = DEBUG|INFO|WARNING|ERROR (默认 INFO)
117
+ GITINSTALL_LOG_JSON = 1|0 (默认 0)
118
+ GITINSTALL_LOG_FILE = 1|0 (默认 1)
119
+
120
+ Args:
121
+ level: 日志级别
122
+ json_mode: 是否使用 JSON 格式输出
123
+ log_file: 是否写入文件
124
+ max_bytes: 单个日志文件最大字节数
125
+ backup_count: 保留的旧日志文件数量
126
+ """
127
+ global _configured
128
+ if _configured:
129
+ return
130
+ with _config_lock:
131
+ if _configured:
132
+ return
133
+
134
+ # 从环境变量读取配置
135
+ env_level = os.environ.get("GITINSTALL_LOG_LEVEL", "INFO").upper()
136
+ env_json = os.environ.get("GITINSTALL_LOG_JSON", "0") == "1"
137
+ env_file = os.environ.get("GITINSTALL_LOG_FILE", "1") == "1"
138
+
139
+ log_level = getattr(logging, level or env_level, logging.INFO)
140
+ use_json = json_mode if json_mode is not None else env_json
141
+ use_file = log_file if log_file is not None else env_file
142
+
143
+ root = logging.getLogger("gitinstall")
144
+ root.setLevel(log_level)
145
+
146
+ # 清除已有 handler(防止重复配置)
147
+ root.handlers.clear()
148
+
149
+ # stderr handler(人类可读格式,始终附加)
150
+ stderr_handler = logging.StreamHandler(sys.stderr)
151
+ stderr_handler.setFormatter(StructuredFormatter(json_mode=False))
152
+ stderr_handler.setLevel(log_level)
153
+ root.addHandler(stderr_handler)
154
+
155
+ # 文件 handler(JSON 格式,带轮转)
156
+ if use_file:
157
+ try:
158
+ _ensure_log_dir()
159
+ file_handler = logging.handlers.RotatingFileHandler(
160
+ LOG_DIR / "gitinstall.log",
161
+ maxBytes=max_bytes,
162
+ backupCount=backup_count,
163
+ encoding="utf-8",
164
+ )
165
+ file_handler.setFormatter(StructuredFormatter(json_mode=True))
166
+ file_handler.setLevel(logging.DEBUG) # 文件记录所有级别
167
+ root.addHandler(file_handler)
168
+ # 硬化日志文件权限
169
+ log_path = LOG_DIR / "gitinstall.log"
170
+ if log_path.exists():
171
+ try:
172
+ os.chmod(log_path, 0o600)
173
+ except OSError:
174
+ pass
175
+ except (OSError, PermissionError):
176
+ pass # 无法写文件时静默降级
177
+
178
+ _configured = True
179
+
180
+
181
+ def get_logger(name: str, **default_extra) -> StructuredLoggerAdapter:
182
+ """
183
+ 获取结构化日志记录器。
184
+
185
+ Args:
186
+ name: 模块名(建议用 __name__)
187
+ **default_extra: 每条日志自动附加的字段
188
+
189
+ Returns:
190
+ StructuredLoggerAdapter 实例
191
+
192
+ 用法:
193
+ logger = get_logger(__name__)
194
+ logger.info("安装完成", project="foo", duration=12.3)
195
+ logger.error("安装失败", project="bar", exc_info=True)
196
+ """
197
+ # 自动配置(首次调用时)
198
+ configure()
199
+
200
+ # 规范化名称:确保在 gitinstall 命名空间下
201
+ if not name.startswith("gitinstall."):
202
+ # 从 tools/xxx.py 提取模块名
203
+ short = name.rsplit(".", 1)[-1] if "." in name else name
204
+ full_name = f"gitinstall.{short}"
205
+ else:
206
+ full_name = name
207
+
208
+ underlying = logging.getLogger(full_name)
209
+ return StructuredLoggerAdapter(underlying, default_extra)
210
+
211
+
212
+ # ── 便捷接口:CLI 进度输出 ──
213
+ # 用于替换 print(..., file=sys.stderr) 的进度消息
214
+
215
+ def progress(msg: str, **extra):
216
+ """输出进度信息到 stderr(替代 print(msg, file=sys.stderr))"""
217
+ logger = get_logger("gitinstall.cli")
218
+ logger.info(msg, **extra)
219
+
220
+
221
+ def debug(msg: str, **extra):
222
+ """输出调试信息"""
223
+ logger = get_logger("gitinstall.cli")
224
+ logger.debug(msg, **extra)
225
+
226
+
227
+ def warn(msg: str, **extra):
228
+ """输出警告信息"""
229
+ logger = get_logger("gitinstall.cli")
230
+ logger.warning(msg, **extra)
231
+
232
+
233
+ def error(msg: str, **extra):
234
+ """输出错误信息"""
235
+ logger = get_logger("gitinstall.cli")
236
+ logger.error(msg, **extra)