clapp-pm 1.0.10__py3-none-any.whl → 1.0.13__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.
- backup_current/build_index.py +132 -0
- backup_current/check_env.py +133 -0
- backup_current/clapp_core.py +61 -0
- backup_current/clean_command.py +214 -0
- backup_current/cli_commands.py +404 -0
- backup_current/dependency_resolver.py +272 -0
- backup_current/doctor_command.py +239 -0
- backup_current/info_command.py +194 -0
- backup_current/install_command.py +236 -0
- backup_current/installer.py +323 -0
- backup_current/list_command.py +262 -0
- backup_current/main.py +294 -0
- backup_current/manifest_schema.py +84 -0
- backup_current/manifest_validator.py +245 -0
- backup_current/package_registry.py +127 -0
- backup_current/package_runner.py +85 -0
- backup_current/post_install_hint.py +144 -0
- backup_current/publish_command.py +253 -0
- backup_current/remote_registry.py +285 -0
- backup_current/setup.py +160 -0
- backup_current/system_test.py +477 -0
- backup_current/uninstall_command.py +215 -0
- backup_current/validate_command.py +225 -0
- backup_current/version.py +8 -0
- backup_current/version_command.py +145 -0
- backup_current/where_command.py +207 -0
- check_env.py +1 -8
- clapp-packages-repo/packages/hello-python/main.py +0 -49
- clapp-packages-repo/packages/hello-python/manifest.json +0 -8
- {clapp_pm-1.0.10.data → clapp_pm-1.0.13.data}/data/version.json +1 -1
- {clapp_pm-1.0.10.dist-info → clapp_pm-1.0.13.dist-info}/METADATA +1 -1
- clapp_pm-1.0.13.dist-info/RECORD +71 -0
- {clapp_pm-1.0.10.dist-info → clapp_pm-1.0.13.dist-info}/top_level.txt +1 -0
- doctor_command.py +0 -1
- install_command.py +3 -0
- version.py +1 -1
- clapp_pm-1.0.10.dist-info/RECORD +0 -45
- {clapp_pm-1.0.10.dist-info → clapp_pm-1.0.13.dist-info}/WHEEL +0 -0
- {clapp_pm-1.0.10.dist-info → clapp_pm-1.0.13.dist-info}/entry_points.txt +0 -0
- {clapp_pm-1.0.10.dist-info → clapp_pm-1.0.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,239 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
doctor_command.py - Kapsamlı sistem tanılaması modülü
|
4
|
+
|
5
|
+
Bu modül `clapp doctor` komutunu destekler ve sistemin
|
6
|
+
clapp için uygun olup olmadığını kapsamlı şekilde kontrol eder.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import sys
|
11
|
+
import shutil
|
12
|
+
import platform
|
13
|
+
import subprocess
|
14
|
+
from pathlib import Path
|
15
|
+
from check_env import (
|
16
|
+
check_python_version,
|
17
|
+
check_clapp_in_path,
|
18
|
+
check_platform_info,
|
19
|
+
check_python_executable,
|
20
|
+
check_working_directory,
|
21
|
+
check_apps_directory,
|
22
|
+
check_permissions,
|
23
|
+
)
|
24
|
+
|
25
|
+
def check_clapp_config():
|
26
|
+
"""clapp konfigürasyon dizinini kontrol eder"""
|
27
|
+
home = Path.home()
|
28
|
+
clapp_config_dir = home / ".clapp"
|
29
|
+
|
30
|
+
if clapp_config_dir.exists():
|
31
|
+
return True, f"Konfigürasyon dizini mevcut: {clapp_config_dir}"
|
32
|
+
else:
|
33
|
+
return False, "Konfigürasyon dizini bulunamadı (~/.clapp)"
|
34
|
+
|
35
|
+
def check_path_environment():
|
36
|
+
"""PATH ortam değişkenini detaylı kontrol eder"""
|
37
|
+
path_env = os.environ.get("PATH", "")
|
38
|
+
path_dirs = path_env.split(os.pathsep)
|
39
|
+
|
40
|
+
# Önemli dizinleri kontrol et
|
41
|
+
important_dirs = []
|
42
|
+
|
43
|
+
# Platform'a göre önemli dizinler
|
44
|
+
system = platform.system().lower()
|
45
|
+
if system == "windows":
|
46
|
+
important_dirs = [
|
47
|
+
os.path.join(os.environ.get("APPDATA", ""), "Python", "Scripts"),
|
48
|
+
os.path.join(sys.prefix, "Scripts"),
|
49
|
+
]
|
50
|
+
else:
|
51
|
+
home = Path.home()
|
52
|
+
important_dirs = [
|
53
|
+
str(home / ".local" / "bin"),
|
54
|
+
"/usr/local/bin",
|
55
|
+
"/usr/bin",
|
56
|
+
]
|
57
|
+
|
58
|
+
found_dirs = []
|
59
|
+
missing_dirs = []
|
60
|
+
|
61
|
+
for imp_dir in important_dirs:
|
62
|
+
if imp_dir in path_dirs:
|
63
|
+
found_dirs.append(imp_dir)
|
64
|
+
else:
|
65
|
+
missing_dirs.append(imp_dir)
|
66
|
+
|
67
|
+
if missing_dirs:
|
68
|
+
return False, f"PATH'te eksik dizinler: {', '.join(missing_dirs)}"
|
69
|
+
else:
|
70
|
+
return True, f"PATH uygun ({len(found_dirs)} önemli dizin mevcut)"
|
71
|
+
|
72
|
+
def check_dependencies():
|
73
|
+
"""Bağımlılıkları kontrol eder"""
|
74
|
+
dependencies = ["python", "pip"]
|
75
|
+
|
76
|
+
missing = []
|
77
|
+
found = []
|
78
|
+
|
79
|
+
for dep in dependencies:
|
80
|
+
if shutil.which(dep):
|
81
|
+
found.append(dep)
|
82
|
+
else:
|
83
|
+
missing.append(dep)
|
84
|
+
|
85
|
+
if missing:
|
86
|
+
return False, f"Eksik bağımlılıklar: {', '.join(missing)}"
|
87
|
+
else:
|
88
|
+
return True, f"Tüm bağımlılıklar mevcut: {', '.join(found)}"
|
89
|
+
|
90
|
+
def check_disk_space():
|
91
|
+
"""Disk alanını kontrol eder"""
|
92
|
+
try:
|
93
|
+
cwd = Path.cwd()
|
94
|
+
stat = shutil.disk_usage(cwd)
|
95
|
+
|
96
|
+
# GB'ye çevir
|
97
|
+
free_gb = stat.free / (1024**3)
|
98
|
+
total_gb = stat.total / (1024**3)
|
99
|
+
|
100
|
+
if free_gb < 0.5: # 500MB'den az
|
101
|
+
return False, f"Yetersiz disk alanı: {free_gb:.1f}GB boş"
|
102
|
+
else:
|
103
|
+
return True, f"Disk alanı uygun: {free_gb:.1f}GB / {total_gb:.1f}GB"
|
104
|
+
|
105
|
+
except Exception as e:
|
106
|
+
return False, f"Disk alanı kontrol edilemedi: {str(e)}"
|
107
|
+
|
108
|
+
def check_network_access():
|
109
|
+
"""Ağ erişimini kontrol eder"""
|
110
|
+
try:
|
111
|
+
# Basit bir ping testi
|
112
|
+
import socket
|
113
|
+
socket.create_connection(("8.8.8.8", 53), timeout=3)
|
114
|
+
return True, "Ağ erişimi mevcut"
|
115
|
+
except:
|
116
|
+
return False, "Ağ erişimi yok veya sınırlı"
|
117
|
+
|
118
|
+
def check_installed_apps():
|
119
|
+
"""Yüklü uygulamaları kontrol eder"""
|
120
|
+
try:
|
121
|
+
from package_registry import list_packages
|
122
|
+
packages = list_packages()
|
123
|
+
|
124
|
+
if not packages:
|
125
|
+
return True, "Yüklü uygulama yok (normal)"
|
126
|
+
|
127
|
+
# Bozuk uygulamaları kontrol et
|
128
|
+
broken_apps = []
|
129
|
+
for package in packages:
|
130
|
+
if not package.get("name") or not package.get("entry"):
|
131
|
+
broken_apps.append(package.get("name", "Bilinmiyor"))
|
132
|
+
|
133
|
+
if broken_apps:
|
134
|
+
return False, f"Bozuk uygulamalar: {', '.join(broken_apps)}"
|
135
|
+
else:
|
136
|
+
return True, f"{len(packages)} uygulama yüklü (tümü geçerli)"
|
137
|
+
|
138
|
+
except Exception as e:
|
139
|
+
return False, f"Uygulama listesi kontrol edilemedi: {str(e)}"
|
140
|
+
|
141
|
+
def check_python_modules():
|
142
|
+
"""Gerekli Python modüllerini kontrol eder"""
|
143
|
+
required_modules = [
|
144
|
+
("json", "JSON desteği"),
|
145
|
+
("os", "İşletim sistemi arayüzü"),
|
146
|
+
("sys", "Sistem arayüzü"),
|
147
|
+
("pathlib", "Dosya yolu işlemleri"),
|
148
|
+
("subprocess", "Alt süreç yönetimi"),
|
149
|
+
("argparse", "Komut satırı ayrıştırma"),
|
150
|
+
]
|
151
|
+
|
152
|
+
missing = []
|
153
|
+
found = []
|
154
|
+
|
155
|
+
for module_name, description in required_modules:
|
156
|
+
try:
|
157
|
+
__import__(module_name)
|
158
|
+
found.append(module_name)
|
159
|
+
except ImportError:
|
160
|
+
missing.append(f"{module_name} ({description})")
|
161
|
+
|
162
|
+
if missing:
|
163
|
+
return False, f"Eksik Python modülleri: {', '.join(missing)}"
|
164
|
+
else:
|
165
|
+
return True, f"Tüm gerekli modüller mevcut ({len(found)} modül)"
|
166
|
+
|
167
|
+
def check_apps_directory():
|
168
|
+
"""apps/ dizinini kontrol eder, yoksa otomatik oluşturur veya bilgi verir"""
|
169
|
+
apps_dir = Path("apps")
|
170
|
+
if apps_dir.exists():
|
171
|
+
return True, "apps/ dizini mevcut"
|
172
|
+
else:
|
173
|
+
try:
|
174
|
+
apps_dir.mkdir(parents=True, exist_ok=True)
|
175
|
+
return True, "apps/ dizini yoktu, otomatik oluşturuldu (ilk kurulum için normal)"
|
176
|
+
except Exception as e:
|
177
|
+
return False, f"apps/ dizini oluşturulamadı: {e}"
|
178
|
+
|
179
|
+
|
180
|
+
def run_doctor():
|
181
|
+
"""Kapsamlı sistem tanılaması yapar"""
|
182
|
+
print("🩺 clapp Sistem Tanılaması")
|
183
|
+
print("=" * 60)
|
184
|
+
print("Sisteminiz clapp için uygun mu kontrol ediliyor...")
|
185
|
+
print()
|
186
|
+
# Tüm kontroller
|
187
|
+
checks = [
|
188
|
+
("Python Sürümü", check_python_version),
|
189
|
+
("Platform Bilgisi", check_platform_info),
|
190
|
+
("Python Çalıştırılabilir", check_python_executable),
|
191
|
+
("Çalışma Dizini", check_working_directory),
|
192
|
+
("clapp PATH Kontrolü", check_clapp_in_path),
|
193
|
+
("PATH Ortam Değişkeni", check_path_environment),
|
194
|
+
("Sistem Bağımlılıkları", check_dependencies),
|
195
|
+
("Python Modülleri", check_python_modules),
|
196
|
+
("apps/ Dizini", check_apps_directory),
|
197
|
+
("Yüklü Uygulamalar", check_installed_apps),
|
198
|
+
("Yazma İzinleri", check_permissions),
|
199
|
+
("Disk Alanı", check_disk_space),
|
200
|
+
("Ağ Erişimi", check_network_access),
|
201
|
+
("clapp Konfigürasyonu", check_clapp_config),
|
202
|
+
]
|
203
|
+
passed = 0
|
204
|
+
failed = 0
|
205
|
+
warnings = 0
|
206
|
+
results = []
|
207
|
+
for check_name, check_func in checks:
|
208
|
+
try:
|
209
|
+
ok, msg = check_func()
|
210
|
+
if ok:
|
211
|
+
print(f"✅ {check_name}: {msg}")
|
212
|
+
passed += 1
|
213
|
+
else:
|
214
|
+
print(f"❌ {check_name}: {msg}")
|
215
|
+
failed += 1
|
216
|
+
results.append((check_name, ok, msg))
|
217
|
+
except Exception as e:
|
218
|
+
print(f"❌ {check_name}: Kontrol sırasında hata: {e}")
|
219
|
+
failed += 1
|
220
|
+
results.append((check_name, False, str(e)))
|
221
|
+
print("\n" + "=" * 60)
|
222
|
+
print(f"📊 Tanılama Özeti:")
|
223
|
+
print(f"✅ Başarılı: {passed}")
|
224
|
+
print(f"❌ Başarısız: {failed}")
|
225
|
+
print(f"⚠️ Uyarı: {warnings}")
|
226
|
+
if failed > 0:
|
227
|
+
print("\n🔧 Dikkat! Bazı sorunlar bulundu.")
|
228
|
+
print("❌ Aşağıdaki sorunları çözmeniz önerilir:")
|
229
|
+
for name, ok, msg in results:
|
230
|
+
if not ok:
|
231
|
+
print(f"\n🔧 {name}:\n Sorun: {msg}")
|
232
|
+
else:
|
233
|
+
print("\n🚀 Her şey yolunda! clapp sorunsuz çalışabilir.")
|
234
|
+
print("\n📞 Yardım:")
|
235
|
+
print("• GitHub: https://github.com/user/clapp")
|
236
|
+
print("• Dokümantasyon: README.md dosyasını okuyun")
|
237
|
+
|
238
|
+
if __name__ == "__main__":
|
239
|
+
run_doctor()
|
@@ -0,0 +1,194 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
info_command.py - Uygulama bilgi görüntüleme modülü
|
4
|
+
|
5
|
+
Bu modül `clapp info <app_name>` komutunu destekler ve
|
6
|
+
yüklü uygulamaların detaylı bilgilerini gösterir.
|
7
|
+
"""
|
8
|
+
|
9
|
+
import os
|
10
|
+
import json
|
11
|
+
from pathlib import Path
|
12
|
+
from package_registry import get_manifest
|
13
|
+
|
14
|
+
def format_field(name, value, width=20):
|
15
|
+
"""Alanı formatlar"""
|
16
|
+
if value is None or value == "":
|
17
|
+
value = "Belirtilmemiş"
|
18
|
+
return f"{name:<{width}}: {value}"
|
19
|
+
|
20
|
+
def format_list(items, indent=22):
|
21
|
+
"""Liste öğelerini formatlar"""
|
22
|
+
if not items:
|
23
|
+
return "Yok"
|
24
|
+
|
25
|
+
if len(items) == 1:
|
26
|
+
return items[0]
|
27
|
+
|
28
|
+
result = items[0]
|
29
|
+
for item in items[1:]:
|
30
|
+
result += f"\n{' ' * indent}{item}"
|
31
|
+
return result
|
32
|
+
|
33
|
+
def get_app_file_info(app_name):
|
34
|
+
"""Uygulama dosya bilgilerini toplar"""
|
35
|
+
apps_dir = Path("apps")
|
36
|
+
app_dir = apps_dir / app_name
|
37
|
+
|
38
|
+
if not app_dir.exists():
|
39
|
+
return None
|
40
|
+
|
41
|
+
info = {
|
42
|
+
"path": str(app_dir.absolute()),
|
43
|
+
"size": 0,
|
44
|
+
"file_count": 0,
|
45
|
+
"files": []
|
46
|
+
}
|
47
|
+
|
48
|
+
try:
|
49
|
+
for file_path in app_dir.rglob("*"):
|
50
|
+
if file_path.is_file():
|
51
|
+
info["file_count"] += 1
|
52
|
+
info["size"] += file_path.stat().st_size
|
53
|
+
info["files"].append(file_path.name)
|
54
|
+
except Exception:
|
55
|
+
pass
|
56
|
+
|
57
|
+
return info
|
58
|
+
|
59
|
+
def format_size(size_bytes):
|
60
|
+
"""Dosya boyutunu formatlar"""
|
61
|
+
if size_bytes == 0:
|
62
|
+
return "0 B"
|
63
|
+
|
64
|
+
for unit in ['B', 'KB', 'MB', 'GB']:
|
65
|
+
if size_bytes < 1024.0:
|
66
|
+
return f"{size_bytes:.1f} {unit}"
|
67
|
+
size_bytes /= 1024.0
|
68
|
+
|
69
|
+
return f"{size_bytes:.1f} TB"
|
70
|
+
|
71
|
+
def show_app_info(app_name):
|
72
|
+
"""Uygulama bilgilerini gösterir"""
|
73
|
+
print(f"📋 {app_name} - Uygulama Bilgileri")
|
74
|
+
print("=" * 50)
|
75
|
+
|
76
|
+
# Manifest bilgilerini al
|
77
|
+
manifest = get_manifest(app_name)
|
78
|
+
|
79
|
+
if not manifest:
|
80
|
+
print(f"❌ '{app_name}' uygulaması bulunamadı veya manifest.json eksik.")
|
81
|
+
print()
|
82
|
+
print("🔧 Öneriler:")
|
83
|
+
print("• Uygulama adını kontrol edin")
|
84
|
+
print("• apps/ dizininde uygulama klasörünün var olduğundan emin olun")
|
85
|
+
print("• manifest.json dosyasının mevcut olduğundan emin olun")
|
86
|
+
print("• Doğrulama için: clapp validate apps/[app_name]")
|
87
|
+
return False
|
88
|
+
|
89
|
+
# Temel bilgiler
|
90
|
+
print("📦 Temel Bilgiler:")
|
91
|
+
print(format_field("Ad", manifest.get("name", app_name)))
|
92
|
+
print(format_field("Sürüm", manifest.get("version", "Belirtilmemiş")))
|
93
|
+
print(format_field("Dil", manifest.get("language", "Belirtilmemiş")))
|
94
|
+
print(format_field("Giriş Dosyası", manifest.get("entry", "Belirtilmemiş")))
|
95
|
+
print()
|
96
|
+
|
97
|
+
# Açıklama
|
98
|
+
description = manifest.get("description", "")
|
99
|
+
if description:
|
100
|
+
print("📝 Açıklama:")
|
101
|
+
print(f" {description}")
|
102
|
+
print()
|
103
|
+
|
104
|
+
# Bağımlılıklar
|
105
|
+
dependencies = manifest.get("dependencies", [])
|
106
|
+
print("🔗 Bağımlılıklar:")
|
107
|
+
if dependencies:
|
108
|
+
for dep in dependencies:
|
109
|
+
print(f" • {dep}")
|
110
|
+
else:
|
111
|
+
print(" Bağımlılık yok")
|
112
|
+
print()
|
113
|
+
|
114
|
+
# Dosya bilgileri
|
115
|
+
file_info = get_app_file_info(app_name)
|
116
|
+
if file_info:
|
117
|
+
print("📁 Dosya Bilgileri:")
|
118
|
+
print(format_field("Konum", file_info["path"]))
|
119
|
+
print(format_field("Dosya Sayısı", file_info["file_count"]))
|
120
|
+
print(format_field("Toplam Boyut", format_size(file_info["size"])))
|
121
|
+
print()
|
122
|
+
|
123
|
+
# Dosya listesi (ilk 10 dosya)
|
124
|
+
if file_info["files"]:
|
125
|
+
print("📄 Dosyalar:")
|
126
|
+
files_to_show = file_info["files"][:10]
|
127
|
+
for file_name in files_to_show:
|
128
|
+
print(f" • {file_name}")
|
129
|
+
|
130
|
+
if len(file_info["files"]) > 10:
|
131
|
+
print(f" ... ve {len(file_info['files']) - 10} dosya daha")
|
132
|
+
print()
|
133
|
+
|
134
|
+
# Ek bilgiler
|
135
|
+
extra_fields = ["author", "license", "homepage", "repository"]
|
136
|
+
extra_info = []
|
137
|
+
|
138
|
+
for field in extra_fields:
|
139
|
+
if field in manifest:
|
140
|
+
extra_info.append((field.title(), manifest[field]))
|
141
|
+
|
142
|
+
if extra_info:
|
143
|
+
print("ℹ️ Ek Bilgiler:")
|
144
|
+
for field_name, value in extra_info:
|
145
|
+
print(format_field(field_name, value))
|
146
|
+
print()
|
147
|
+
|
148
|
+
# Çalıştırma bilgisi
|
149
|
+
print("🚀 Çalıştırma:")
|
150
|
+
print(f" clapp run {app_name}")
|
151
|
+
|
152
|
+
# Doğrulama önerisi
|
153
|
+
print()
|
154
|
+
print("🔧 Doğrulama:")
|
155
|
+
print(f" clapp validate apps/{app_name}")
|
156
|
+
|
157
|
+
return True
|
158
|
+
|
159
|
+
def list_all_apps_info():
|
160
|
+
"""Tüm uygulamaların kısa bilgilerini listeler"""
|
161
|
+
from package_registry import list_packages
|
162
|
+
|
163
|
+
packages = list_packages()
|
164
|
+
|
165
|
+
if not packages:
|
166
|
+
print("📦 Yüklü uygulama bulunamadı.")
|
167
|
+
return
|
168
|
+
|
169
|
+
print("📋 Yüklü Uygulamalar - Özet Bilgiler")
|
170
|
+
print("=" * 60)
|
171
|
+
|
172
|
+
for package in packages:
|
173
|
+
name = package.get("name", "Bilinmiyor")
|
174
|
+
version = package.get("version", "?")
|
175
|
+
language = package.get("language", "?")
|
176
|
+
description = package.get("description", "Açıklama yok")
|
177
|
+
|
178
|
+
# Açıklamayı kısalt
|
179
|
+
if len(description) > 40:
|
180
|
+
description = description[:37] + "..."
|
181
|
+
|
182
|
+
print(f"📦 {name} (v{version})")
|
183
|
+
print(f" 🔧 Dil: {language}")
|
184
|
+
print(f" 📝 {description}")
|
185
|
+
print()
|
186
|
+
|
187
|
+
if __name__ == "__main__":
|
188
|
+
import sys
|
189
|
+
|
190
|
+
if len(sys.argv) > 1:
|
191
|
+
app_name = sys.argv[1]
|
192
|
+
show_app_info(app_name)
|
193
|
+
else:
|
194
|
+
list_all_apps_info()
|
@@ -0,0 +1,236 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
"""
|
3
|
+
install_command.py - clapp Install Command
|
4
|
+
|
5
|
+
Bu modül 'clapp install <app_name>' komutunu uygular.
|
6
|
+
Index.json'dan uygulama bilgilerini alıp GitHub'dan indirerek
|
7
|
+
yerel apps/ klasörüne kurar.
|
8
|
+
"""
|
9
|
+
|
10
|
+
import os
|
11
|
+
import json
|
12
|
+
import shutil
|
13
|
+
import requests
|
14
|
+
import zipfile
|
15
|
+
import tempfile
|
16
|
+
import sys
|
17
|
+
from pathlib import Path
|
18
|
+
from typing import Tuple, Optional, Dict, Any
|
19
|
+
|
20
|
+
from manifest_validator import validate_manifest_verbose
|
21
|
+
|
22
|
+
def get_apps_directory() -> str:
|
23
|
+
"""Uygulamaların kurulacağı dizini döndürür"""
|
24
|
+
# Kullanıcının home dizininde .clapp klasörü oluştur
|
25
|
+
home_dir = Path.home()
|
26
|
+
clapp_dir = home_dir / ".clapp"
|
27
|
+
apps_dir = clapp_dir / "apps"
|
28
|
+
|
29
|
+
# Klasörleri oluştur
|
30
|
+
apps_dir.mkdir(parents=True, exist_ok=True)
|
31
|
+
|
32
|
+
return str(apps_dir)
|
33
|
+
|
34
|
+
def load_index(index_path: str = "index.json") -> Tuple[bool, str, Optional[list]]:
|
35
|
+
"""
|
36
|
+
Index.json dosyasını yükler
|
37
|
+
|
38
|
+
Returns:
|
39
|
+
(success, message, apps_list)
|
40
|
+
"""
|
41
|
+
try:
|
42
|
+
# Yerel index.json'u kontrol et
|
43
|
+
if os.path.exists(index_path):
|
44
|
+
with open(index_path, 'r', encoding='utf-8') as f:
|
45
|
+
apps = json.load(f)
|
46
|
+
return True, "Yerel index yüklendi", apps
|
47
|
+
|
48
|
+
# GitHub'dan index.json'u indir
|
49
|
+
print("🔄 GitHub'dan index.json indiriliyor...")
|
50
|
+
url = "https://raw.githubusercontent.com/mburakmmm/clapp-packages/main/index.json"
|
51
|
+
response = requests.get(url, timeout=10)
|
52
|
+
|
53
|
+
if response.status_code == 200:
|
54
|
+
apps = response.json()
|
55
|
+
# Yerel kopyasını kaydet
|
56
|
+
with open(index_path, 'w', encoding='utf-8') as f:
|
57
|
+
json.dump(apps, f, indent=2, ensure_ascii=False)
|
58
|
+
return True, "GitHub'dan index indirildi", apps
|
59
|
+
else:
|
60
|
+
return False, f"GitHub'dan index indirilemedi: {response.status_code}", None
|
61
|
+
|
62
|
+
except requests.RequestException as e:
|
63
|
+
return False, f"Network hatası: {e}", None
|
64
|
+
except json.JSONDecodeError as e:
|
65
|
+
return False, f"JSON parse hatası: {e}", None
|
66
|
+
except Exception as e:
|
67
|
+
return False, f"Index yükleme hatası: {e}", None
|
68
|
+
|
69
|
+
def find_app_in_index(app_name: str, apps: list) -> Optional[Dict[str, Any]]:
|
70
|
+
"""Index'te uygulama arar"""
|
71
|
+
for app in apps:
|
72
|
+
if app.get('name') == app_name:
|
73
|
+
return app
|
74
|
+
return None
|
75
|
+
|
76
|
+
def download_app_from_github(app_info: Dict[str, Any], temp_dir: str) -> Tuple[bool, str]:
|
77
|
+
"""
|
78
|
+
GitHub'dan uygulama dosyalarını indirir
|
79
|
+
|
80
|
+
Returns:
|
81
|
+
(success, message)
|
82
|
+
"""
|
83
|
+
try:
|
84
|
+
repo_url = app_info.get('repo_url', 'https://github.com/mburakmmm/clapp-packages')
|
85
|
+
subdir = app_info.get('subdir', app_info['name'])
|
86
|
+
|
87
|
+
# GitHub zip URL'si oluştur
|
88
|
+
if 'github.com' in repo_url:
|
89
|
+
# https://github.com/user/repo -> https://github.com/user/repo/archive/refs/heads/main.zip
|
90
|
+
zip_url = repo_url + "/archive/refs/heads/main.zip"
|
91
|
+
else:
|
92
|
+
return False, f"Desteklenmeyen repo URL: {repo_url}"
|
93
|
+
|
94
|
+
print(f"📥 İndiriliyor: {zip_url}")
|
95
|
+
|
96
|
+
# Zip dosyasını indir
|
97
|
+
response = requests.get(zip_url, timeout=30)
|
98
|
+
if response.status_code != 200:
|
99
|
+
return False, f"İndirme hatası: {response.status_code}"
|
100
|
+
|
101
|
+
# Zip dosyasını geçici klasöre kaydet
|
102
|
+
zip_path = os.path.join(temp_dir, "repo.zip")
|
103
|
+
with open(zip_path, 'wb') as f:
|
104
|
+
f.write(response.content)
|
105
|
+
|
106
|
+
# Zip'i aç
|
107
|
+
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
108
|
+
zip_ref.extractall(temp_dir)
|
109
|
+
|
110
|
+
# Çıkarılan klasörü bul (genellikle repo-main formatında)
|
111
|
+
extracted_folders = [d for d in os.listdir(temp_dir) if os.path.isdir(os.path.join(temp_dir, d))]
|
112
|
+
if not extracted_folders:
|
113
|
+
return False, "Çıkarılan klasör bulunamadı"
|
114
|
+
|
115
|
+
extracted_folder = extracted_folders[0]
|
116
|
+
app_source_path = os.path.join(temp_dir, extracted_folder, subdir)
|
117
|
+
|
118
|
+
if not os.path.exists(app_source_path):
|
119
|
+
return False, f"Uygulama klasörü bulunamadı: {subdir}"
|
120
|
+
|
121
|
+
return True, app_source_path
|
122
|
+
|
123
|
+
except Exception as e:
|
124
|
+
return False, f"İndirme hatası: {e}"
|
125
|
+
|
126
|
+
def install_app_locally(app_name: str, source_path: str) -> Tuple[bool, str]:
|
127
|
+
"""
|
128
|
+
Uygulamayı yerel apps klasörüne kurar
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
(success, message)
|
132
|
+
"""
|
133
|
+
try:
|
134
|
+
apps_dir = get_apps_directory()
|
135
|
+
target_path = os.path.join(apps_dir, app_name)
|
136
|
+
|
137
|
+
# Hedef klasör varsa sil
|
138
|
+
if os.path.exists(target_path):
|
139
|
+
shutil.rmtree(target_path)
|
140
|
+
print(f"⚠️ Mevcut {app_name} kaldırıldı")
|
141
|
+
|
142
|
+
# Kopyala
|
143
|
+
shutil.copytree(source_path, target_path)
|
144
|
+
|
145
|
+
# Manifest'i doğrula
|
146
|
+
manifest_path = os.path.join(target_path, "manifest.json")
|
147
|
+
if os.path.exists(manifest_path):
|
148
|
+
with open(manifest_path, 'r', encoding='utf-8') as f:
|
149
|
+
manifest = json.load(f)
|
150
|
+
|
151
|
+
is_valid, errors = validate_manifest_verbose(manifest)
|
152
|
+
if not is_valid:
|
153
|
+
shutil.rmtree(target_path)
|
154
|
+
return False, f"Manifest doğrulama hatası: {errors}"
|
155
|
+
|
156
|
+
return True, f"Uygulama kuruldu: {target_path}"
|
157
|
+
|
158
|
+
except Exception as e:
|
159
|
+
return False, f"Kurulum hatası: {e}"
|
160
|
+
|
161
|
+
def install_app(app_name: str) -> Tuple[bool, str]:
|
162
|
+
"""
|
163
|
+
Ana install fonksiyonu
|
164
|
+
|
165
|
+
Args:
|
166
|
+
app_name: Kurulacak uygulamanın adı
|
167
|
+
|
168
|
+
Returns:
|
169
|
+
(success, message)
|
170
|
+
"""
|
171
|
+
print(f"🚀 Kurulum başlatılıyor: {app_name}")
|
172
|
+
print("=" * 50)
|
173
|
+
|
174
|
+
# 1. Index'i yükle
|
175
|
+
print("1️⃣ Index yükleniyor...")
|
176
|
+
index_success, index_message, apps = load_index()
|
177
|
+
|
178
|
+
if not index_success:
|
179
|
+
return False, f"Index yükleme hatası: {index_message}"
|
180
|
+
|
181
|
+
print(f"✅ {len(apps)} uygulama listelendi")
|
182
|
+
|
183
|
+
# 2. Uygulamayı index'te ara
|
184
|
+
print("2️⃣ Uygulama aranıyor...")
|
185
|
+
app_info = find_app_in_index(app_name, apps)
|
186
|
+
|
187
|
+
if not app_info:
|
188
|
+
available_apps = [app['name'] for app in apps]
|
189
|
+
return False, f"Uygulama bulunamadı: {app_name}\nMevcut uygulamalar: {', '.join(available_apps)}"
|
190
|
+
|
191
|
+
print(f"✅ {app_name} v{app_info['version']} bulundu")
|
192
|
+
|
193
|
+
# 3. Geçici klasör oluştur
|
194
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
195
|
+
print("3️⃣ Uygulama indiriliyor...")
|
196
|
+
|
197
|
+
# GitHub'dan indir
|
198
|
+
download_success, download_result = download_app_from_github(app_info, temp_dir)
|
199
|
+
|
200
|
+
if not download_success:
|
201
|
+
return False, f"İndirme hatası: {download_result}"
|
202
|
+
|
203
|
+
source_path = download_result
|
204
|
+
print(f"✅ İndirme tamamlandı")
|
205
|
+
|
206
|
+
# 4. Yerel kurulum
|
207
|
+
print("4️⃣ Uygulama kuruluyor...")
|
208
|
+
install_success, install_message = install_app_locally(app_name, source_path)
|
209
|
+
|
210
|
+
if not install_success:
|
211
|
+
return False, install_message
|
212
|
+
|
213
|
+
return True, f"🎉 '{app_name}' başarıyla kuruldu!"
|
214
|
+
|
215
|
+
def main():
|
216
|
+
"""CLI entry point"""
|
217
|
+
if len(sys.argv) < 2:
|
218
|
+
print("Kullanım: python install_command.py <app_name>")
|
219
|
+
print("Örnek: python install_command.py hello-python")
|
220
|
+
sys.exit(1)
|
221
|
+
|
222
|
+
app_name = sys.argv[1]
|
223
|
+
|
224
|
+
success, message = install_app(app_name)
|
225
|
+
|
226
|
+
print("\n" + "=" * 50)
|
227
|
+
if success:
|
228
|
+
print(f"✅ {message}")
|
229
|
+
print(f"📍 Kurulum dizini: {get_apps_directory()}")
|
230
|
+
sys.exit(0)
|
231
|
+
else:
|
232
|
+
print(f"❌ {message}")
|
233
|
+
sys.exit(1)
|
234
|
+
|
235
|
+
if __name__ == "__main__":
|
236
|
+
main()
|