clapp-pm 1.0.9__py3-none-any.whl → 1.0.11__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 (40) hide show
  1. backup_current/build_index.py +132 -0
  2. backup_current/check_env.py +133 -0
  3. backup_current/clapp_core.py +61 -0
  4. backup_current/clean_command.py +214 -0
  5. backup_current/cli_commands.py +404 -0
  6. backup_current/dependency_resolver.py +272 -0
  7. backup_current/doctor_command.py +239 -0
  8. backup_current/info_command.py +194 -0
  9. backup_current/install_command.py +236 -0
  10. backup_current/installer.py +323 -0
  11. backup_current/list_command.py +262 -0
  12. backup_current/main.py +294 -0
  13. backup_current/manifest_schema.py +84 -0
  14. backup_current/manifest_validator.py +245 -0
  15. backup_current/package_registry.py +127 -0
  16. backup_current/package_runner.py +85 -0
  17. backup_current/post_install_hint.py +144 -0
  18. backup_current/publish_command.py +253 -0
  19. backup_current/remote_registry.py +285 -0
  20. backup_current/setup.py +160 -0
  21. backup_current/system_test.py +477 -0
  22. backup_current/uninstall_command.py +215 -0
  23. backup_current/validate_command.py +225 -0
  24. backup_current/version.py +8 -0
  25. backup_current/version_command.py +145 -0
  26. backup_current/where_command.py +207 -0
  27. check_env.py +1 -8
  28. clapp-packages-repo/packages/hello-python/main.py +0 -49
  29. clapp-packages-repo/packages/hello-python/manifest.json +0 -8
  30. {clapp_pm-1.0.9.data → clapp_pm-1.0.11.data}/data/version.json +1 -1
  31. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/METADATA +1 -1
  32. clapp_pm-1.0.11.dist-info/RECORD +71 -0
  33. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/top_level.txt +2 -0
  34. doctor_command.py +0 -1
  35. install_command.py +3 -0
  36. version.py +8 -0
  37. clapp_pm-1.0.9.dist-info/RECORD +0 -44
  38. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/WHEEL +0 -0
  39. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.dist-info}/entry_points.txt +0 -0
  40. {clapp_pm-1.0.9.dist-info → clapp_pm-1.0.11.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()