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
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)
|