flutter-dev 0.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.
- common_utils.py +261 -0
- core/__init__.py +7 -0
- core/constants.py +57 -0
- core/state.py +25 -0
- create_page.py +288 -0
- fdev.py +258 -0
- flutter_dev-0.1.0.dist-info/METADATA +411 -0
- flutter_dev-0.1.0.dist-info/RECORD +30 -0
- flutter_dev-0.1.0.dist-info/WHEEL +5 -0
- flutter_dev-0.1.0.dist-info/entry_points.txt +5 -0
- flutter_dev-0.1.0.dist-info/licenses/LICENSE +21 -0
- flutter_dev-0.1.0.dist-info/top_level.txt +9 -0
- gemini_api.py +395 -0
- git_diff_output_editor.py +34 -0
- install_legacy.py +467 -0
- managers/__init__.py +69 -0
- managers/ai.py +113 -0
- managers/app.py +541 -0
- managers/brew.py +477 -0
- managers/build.py +436 -0
- managers/datetime.py +49 -0
- managers/device.py +207 -0
- managers/doctor.py +286 -0
- managers/git.py +981 -0
- managers/git_account.py +542 -0
- managers/merge.py +165 -0
- managers/mirror.py +205 -0
- managers/project.py +138 -0
- managers/web_deploy.py +43 -0
- switch_ai.py +181 -0
managers/doctor.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Doctor Module - Environment health check for Flutter development
|
|
4
|
+
Checks all required and optional tools, dependencies, and configurations
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import shutil
|
|
10
|
+
import subprocess
|
|
11
|
+
import platform
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from common_utils import RED, GREEN, YELLOW, BLUE, NC, CHECKMARK, CROSS, is_macos, is_windows
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _get_cmd_version(cmd_list, timeout=5):
|
|
18
|
+
"""
|
|
19
|
+
Run a command and return its output (first line).
|
|
20
|
+
Returns None if the command is not found or fails.
|
|
21
|
+
"""
|
|
22
|
+
try:
|
|
23
|
+
result = subprocess.run(
|
|
24
|
+
cmd_list,
|
|
25
|
+
capture_output=True,
|
|
26
|
+
text=True,
|
|
27
|
+
timeout=timeout,
|
|
28
|
+
encoding='utf-8',
|
|
29
|
+
errors='replace',
|
|
30
|
+
)
|
|
31
|
+
output = result.stdout.strip() or result.stderr.strip()
|
|
32
|
+
if result.returncode == 0 and output:
|
|
33
|
+
return output.splitlines()[0]
|
|
34
|
+
return None
|
|
35
|
+
except (FileNotFoundError, subprocess.TimeoutExpired, OSError):
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _extract_version(raw, prefix=None):
|
|
40
|
+
"""Extract version number from a raw version string."""
|
|
41
|
+
if not raw:
|
|
42
|
+
return None
|
|
43
|
+
if prefix:
|
|
44
|
+
raw = raw.replace(prefix, '').strip()
|
|
45
|
+
# Take first token that looks like a version
|
|
46
|
+
import re
|
|
47
|
+
match = re.search(r'[\d]+(?:\.[\d]+)*', raw)
|
|
48
|
+
return match.group(0) if match else raw.strip()
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _check_tool(name, cmd_list, required=True, install_hint=None, version_prefix=None):
|
|
52
|
+
"""
|
|
53
|
+
Check if a tool is installed and return (status, version_str, name).
|
|
54
|
+
status: True = found, False = not found
|
|
55
|
+
"""
|
|
56
|
+
raw = _get_cmd_version(cmd_list)
|
|
57
|
+
if raw:
|
|
58
|
+
version = _extract_version(raw, version_prefix)
|
|
59
|
+
return True, version
|
|
60
|
+
else:
|
|
61
|
+
return False, install_hint
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _check_python_package(package_name):
|
|
65
|
+
"""Check if a Python package is installed and return its version."""
|
|
66
|
+
try:
|
|
67
|
+
mod = __import__(package_name.replace('-', '_'))
|
|
68
|
+
version = getattr(mod, '__version__', None) or getattr(mod, 'VERSION', 'installed')
|
|
69
|
+
return True, str(version)
|
|
70
|
+
except ImportError:
|
|
71
|
+
return False, f"pip install {package_name}"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _check_env_file():
|
|
75
|
+
"""Check .env file existence and API key configuration."""
|
|
76
|
+
script_dir = Path(__file__).parent.parent
|
|
77
|
+
env_path = script_dir / '.env'
|
|
78
|
+
|
|
79
|
+
if not env_path.exists():
|
|
80
|
+
return False, ".env file not found"
|
|
81
|
+
|
|
82
|
+
# Read and check for configured API keys
|
|
83
|
+
configured_keys = []
|
|
84
|
+
default_service = None
|
|
85
|
+
|
|
86
|
+
with open(env_path, 'r', encoding='utf-8') as f:
|
|
87
|
+
for line in f:
|
|
88
|
+
line = line.strip()
|
|
89
|
+
if line.startswith('#') or '=' not in line:
|
|
90
|
+
continue
|
|
91
|
+
key, value = line.split('=', 1)
|
|
92
|
+
key = key.strip()
|
|
93
|
+
value = value.strip()
|
|
94
|
+
|
|
95
|
+
if key == 'DEFAULT_AI_SERVICE' and value:
|
|
96
|
+
default_service = value
|
|
97
|
+
|
|
98
|
+
# Check if API key is actually set (not placeholder)
|
|
99
|
+
if key.endswith('_API_KEY') and value and 'your_' not in value.lower() and 'here' not in value.lower():
|
|
100
|
+
service = key.replace('_API_KEY', '').lower()
|
|
101
|
+
configured_keys.append(service)
|
|
102
|
+
|
|
103
|
+
if configured_keys:
|
|
104
|
+
active = default_service or configured_keys[0]
|
|
105
|
+
return True, f"active: {active} ({len(configured_keys)} key(s) configured)"
|
|
106
|
+
else:
|
|
107
|
+
return False, "no API keys configured"
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _check_flutter_project():
|
|
111
|
+
"""Check if current directory is a Flutter project."""
|
|
112
|
+
pubspec = Path('pubspec.yaml')
|
|
113
|
+
if pubspec.exists():
|
|
114
|
+
# Try to extract project name
|
|
115
|
+
try:
|
|
116
|
+
with open(pubspec, 'r', encoding='utf-8') as f:
|
|
117
|
+
for line in f:
|
|
118
|
+
if line.startswith('name:'):
|
|
119
|
+
name = line.split(':', 1)[1].strip()
|
|
120
|
+
return True, name
|
|
121
|
+
except Exception:
|
|
122
|
+
pass
|
|
123
|
+
return True, "detected"
|
|
124
|
+
return False, "not a Flutter project directory"
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def run_doctor():
|
|
128
|
+
"""Run full environment health check."""
|
|
129
|
+
print(f"\n{BLUE}╔══════════════════════════════════════════════════════╗{NC}")
|
|
130
|
+
print(f"{BLUE}║ fdev doctor - Environment Health Check ║{NC}")
|
|
131
|
+
print(f"{BLUE}╚══════════════════════════════════════════════════════╝{NC}")
|
|
132
|
+
print(f"{BLUE}Platform: {GREEN}{platform.system()} {platform.machine()}{NC}")
|
|
133
|
+
print(f"{BLUE}Python: {GREEN}{platform.python_version()} ({sys.executable}){NC}")
|
|
134
|
+
print()
|
|
135
|
+
|
|
136
|
+
passed = 0
|
|
137
|
+
failed = 0
|
|
138
|
+
warnings = 0
|
|
139
|
+
|
|
140
|
+
def print_result(name, status, detail, required=True, pad=22):
|
|
141
|
+
nonlocal passed, failed, warnings
|
|
142
|
+
label = name.ljust(pad)
|
|
143
|
+
if status:
|
|
144
|
+
print(f" {CHECKMARK} {label} {GREEN}{detail}{NC}")
|
|
145
|
+
passed += 1
|
|
146
|
+
elif required:
|
|
147
|
+
print(f" {CROSS} {label} {RED}{detail}{NC}")
|
|
148
|
+
failed += 1
|
|
149
|
+
else:
|
|
150
|
+
print(f" {YELLOW}!{NC} {label} {YELLOW}{detail}{NC}")
|
|
151
|
+
warnings += 1
|
|
152
|
+
|
|
153
|
+
# ── Core Tools (Required) ──────────────────────────────
|
|
154
|
+
print(f"{BLUE}Core Tools (required):{NC}")
|
|
155
|
+
|
|
156
|
+
status, detail = _check_tool("Flutter", ["flutter", "--version"], version_prefix="Flutter")
|
|
157
|
+
print_result("Flutter SDK", status, detail)
|
|
158
|
+
|
|
159
|
+
status, detail = _check_tool("Dart", ["dart", "--version"], version_prefix="Dart SDK version:")
|
|
160
|
+
print_result("Dart SDK", status, detail)
|
|
161
|
+
|
|
162
|
+
status, detail = _check_tool("Git", ["git", "--version"], version_prefix="git version")
|
|
163
|
+
print_result("Git", status, detail)
|
|
164
|
+
|
|
165
|
+
# Python packages
|
|
166
|
+
status, detail = _check_python_package("dotenv")
|
|
167
|
+
print_result("python-dotenv", status, detail)
|
|
168
|
+
|
|
169
|
+
status, detail = _check_python_package("requests")
|
|
170
|
+
print_result("requests", status, detail)
|
|
171
|
+
|
|
172
|
+
# ── Android Tools ──────────────────────────────────────
|
|
173
|
+
print(f"\n{BLUE}Android Tools:{NC}")
|
|
174
|
+
|
|
175
|
+
status, detail = _check_tool("ADB", ["adb", "--version"])
|
|
176
|
+
if status and detail:
|
|
177
|
+
detail = _extract_version(detail)
|
|
178
|
+
print_result("ADB", status, detail or "not found (install Android SDK platform-tools)", required=False)
|
|
179
|
+
|
|
180
|
+
# Check Java/JDK (needed for Android builds)
|
|
181
|
+
status, detail = _check_tool("Java", ["java", "-version"])
|
|
182
|
+
if not status:
|
|
183
|
+
detail = "not found (install JDK for Android builds)"
|
|
184
|
+
print_result("Java/JDK", status, detail, required=False)
|
|
185
|
+
|
|
186
|
+
# Check connected devices
|
|
187
|
+
if shutil.which("adb"):
|
|
188
|
+
try:
|
|
189
|
+
result = subprocess.run(
|
|
190
|
+
["adb", "devices"], capture_output=True, text=True, timeout=5
|
|
191
|
+
)
|
|
192
|
+
lines = [l for l in result.stdout.strip().splitlines()[1:] if l.strip() and 'device' in l]
|
|
193
|
+
if lines:
|
|
194
|
+
print_result("Connected Devices", True, f"{len(lines)} device(s) connected", required=False)
|
|
195
|
+
else:
|
|
196
|
+
print_result("Connected Devices", False, "no devices connected", required=False)
|
|
197
|
+
except Exception:
|
|
198
|
+
print_result("Connected Devices", False, "could not check", required=False)
|
|
199
|
+
|
|
200
|
+
# ── iOS Tools (macOS only) ─────────────────────────────
|
|
201
|
+
if is_macos():
|
|
202
|
+
print(f"\n{BLUE}iOS Tools (macOS):{NC}")
|
|
203
|
+
|
|
204
|
+
status, detail = _check_tool("Xcode", ["xcodebuild", "-version"])
|
|
205
|
+
print_result("Xcode", status, detail or "not found (install from App Store)", required=False)
|
|
206
|
+
|
|
207
|
+
status, detail = _check_tool("CocoaPods", ["pod", "--version"])
|
|
208
|
+
print_result("CocoaPods", status, detail or "not found (gem install cocoapods)", required=False)
|
|
209
|
+
|
|
210
|
+
# xcrun check
|
|
211
|
+
status, detail = _check_tool("xcrun", ["xcrun", "--version"])
|
|
212
|
+
if status and detail:
|
|
213
|
+
detail = _extract_version(detail)
|
|
214
|
+
print_result("xcrun", status, detail or "not found (install Xcode CLI tools)", required=False)
|
|
215
|
+
|
|
216
|
+
# ── Optional Tools ─────────────────────────────────────
|
|
217
|
+
print(f"\n{BLUE}Optional Tools:{NC}")
|
|
218
|
+
|
|
219
|
+
status, detail = _check_tool("scrcpy", ["scrcpy", "--version"])
|
|
220
|
+
if not status:
|
|
221
|
+
if is_macos():
|
|
222
|
+
detail = "not found (brew install scrcpy)"
|
|
223
|
+
elif is_windows():
|
|
224
|
+
detail = "not found (scoop install scrcpy)"
|
|
225
|
+
else:
|
|
226
|
+
detail = "not found (apt install scrcpy)"
|
|
227
|
+
print_result("scrcpy", status, detail, required=False)
|
|
228
|
+
|
|
229
|
+
status, detail = _check_tool("Firebase CLI", ["firebase", "--version"])
|
|
230
|
+
if not status:
|
|
231
|
+
detail = "not found (npm install -g firebase-tools)"
|
|
232
|
+
print_result("Firebase CLI", status, detail, required=False)
|
|
233
|
+
|
|
234
|
+
# GitHub CLI (needed for `fdev git` account manager)
|
|
235
|
+
status, detail = _check_tool("gh", ["gh", "--version"])
|
|
236
|
+
if not status:
|
|
237
|
+
if is_macos():
|
|
238
|
+
detail = "not found (brew install gh)"
|
|
239
|
+
elif is_windows():
|
|
240
|
+
detail = "not found (scoop install gh)"
|
|
241
|
+
else:
|
|
242
|
+
detail = "not found (see https://cli.github.com)"
|
|
243
|
+
print_result("GitHub CLI", status, detail, required=False)
|
|
244
|
+
|
|
245
|
+
# VSCode CLI
|
|
246
|
+
status, detail = _check_tool("VSCode", ["code", "--version"])
|
|
247
|
+
if status and detail:
|
|
248
|
+
detail = detail.splitlines()[0]
|
|
249
|
+
if not status:
|
|
250
|
+
detail = "not found (install VSCode + shell command)"
|
|
251
|
+
print_result("VSCode CLI", status, detail, required=False)
|
|
252
|
+
|
|
253
|
+
# ── Configuration ──────────────────────────────────────
|
|
254
|
+
print(f"\n{BLUE}Configuration:{NC}")
|
|
255
|
+
|
|
256
|
+
status, detail = _check_env_file()
|
|
257
|
+
print_result(".env File", status, detail)
|
|
258
|
+
|
|
259
|
+
status, detail = _check_flutter_project()
|
|
260
|
+
print_result("Flutter Project", status, detail, required=False)
|
|
261
|
+
|
|
262
|
+
# Check if fdev is globally accessible
|
|
263
|
+
fdev_path = shutil.which("fdev")
|
|
264
|
+
if fdev_path:
|
|
265
|
+
print_result("fdev Global", True, fdev_path, required=False)
|
|
266
|
+
else:
|
|
267
|
+
print_result("fdev Global", False, "not in PATH (run setup.py)", required=False)
|
|
268
|
+
|
|
269
|
+
# ── Summary ────────────────────────────────────────────
|
|
270
|
+
print(f"\n{BLUE}{'─' * 54}{NC}")
|
|
271
|
+
total = passed + failed + warnings
|
|
272
|
+
summary_parts = []
|
|
273
|
+
if passed:
|
|
274
|
+
summary_parts.append(f"{GREEN}{passed} passed{NC}")
|
|
275
|
+
if warnings:
|
|
276
|
+
summary_parts.append(f"{YELLOW}{warnings} warning(s){NC}")
|
|
277
|
+
if failed:
|
|
278
|
+
summary_parts.append(f"{RED}{failed} issue(s){NC}")
|
|
279
|
+
print(f" {' | '.join(summary_parts)} (total: {total} checks)")
|
|
280
|
+
|
|
281
|
+
if failed == 0:
|
|
282
|
+
print(f"\n {GREEN}All required tools are installed! Happy coding!{NC}")
|
|
283
|
+
else:
|
|
284
|
+
print(f"\n {YELLOW}Fix the issues above to get full fdev functionality.{NC}")
|
|
285
|
+
|
|
286
|
+
print()
|