clapp-pm 1.0.0__py3-none-any.whl → 1.0.2__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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "app_name": "clapp",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "author": "Melih Burak Memiş",
5
5
  "description": "Basit ve güçlü paket yöneticisi",
6
6
  "source": "https://github.com/melihburak/clapp",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clapp-pm
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Lightweight cross-language app manager for Python and Lua
5
5
  Home-page: https://github.com/mburakmmm/clapp
6
6
  Author: Melih Burak Memiş
@@ -3,16 +3,20 @@ clapp_core.py,sha256=5tkzt7J4GKSv7Chv5Zoe9N0dv_sWlYGqYxmIn3dhEPY,1764
3
3
  clean_command.py,sha256=nmzitkdNo0Ovgi-tGPUxa9mkAIFzwEzGNaTm82MUdvw,6806
4
4
  cli_commands.py,sha256=ffJeRJTeipzHkSXhIUKrGzmymqtuv9bKCCQ2WGMQUD4,12482
5
5
  dependency_resolver.py,sha256=slskJhZJ5rRIv0Gcefd-cakHxdYl45bPwE4s_0APpsA,8562
6
- doctor_command.py,sha256=VpYx5isO-EI9EmCqlyAvGIJWasMaDDLmU7MDYBOz3kc,9446
6
+ doctor_command.py,sha256=jLnvpU9Oglq553bwgF7fhAcUChZTMD2Qle9ZxNHPefc,7680
7
7
  info_command.py,sha256=b74Pl1-x_PSezALx-lS3FkEcVNTF7a9McTKk5XTDhjM,5629
8
- installer.py,sha256=4ojp3FCTw9-VxJhBzadO9oNBFWDHw5IQsMqXlwvKH_g,10039
8
+ install_command.py,sha256=tkEMuezZ_-GWCikMfzZzkL0OjnRcyNixtW9OC749GDw,7789
9
+ installer.py,sha256=EkQ5Z1Cq_Z5u_Ssv2Nc8hryI5E97x4D1QVjSqEHe0dE,10619
10
+ list_command.py,sha256=qbeocvrg2eXRklxbYS3audQhYHGXTlMBk_tNh1cMxd0,8391
9
11
  main.py,sha256=aLwnLGv5AOjRsEBCyJKANKFKccpGEEo8PNe2_Mgm7w4,11806
10
12
  manifest_schema.py,sha256=IxfKuYgcIhILJrDMOm5vjSJn2jp7hPpUoxtjPMCPvbE,2201
11
13
  manifest_validator.py,sha256=MTI6c_sYfVakQ6aQUu5_qkukTh4H1FcSrT4uRdE6xIg,7990
12
14
  package_registry.py,sha256=LVu_IHHFxEvAsx7WxX04MD-jGXhRtwJzGTqDIieN9ao,4043
13
15
  package_runner.py,sha256=1aKQXTdHqfKFC0jZZ4qv_Io2-JS_WM6fWMwT7iunM2c,2668
14
16
  post_install_hint.py,sha256=wjMPCgRurZiGu6hv_se-XA36KqBCdeYdRCD1q7FrJzQ,4918
17
+ publish_command.py,sha256=P05AFbu_mxcc1yAiwShN5Yi9PX1o_7TFXD1mowJcqJE,8589
15
18
  remote_registry.py,sha256=rPBIM_ESXUt0br5cARQ4YbzUoTda0G4e1KGzfyYMbpQ,8235
19
+ uninstall_command.py,sha256=rQYbZ-XMw8Xxw1fmgGdDaBQmgBGqyJ_rTBZkvEV5HV0,7066
16
20
  validate_command.py,sha256=idaujErzrwuZNT6DYCVTVwZqBDEEi1GTxIXAGBgKMKM,7623
17
21
  version_command.py,sha256=Zk0EIUc2TAj1bN7EHpTqWmI3m-mH9sUpRz3T828ovvY,4370
18
22
  where_command.py,sha256=TcLoXLGmrPSHQuvlceVuuKBsfeadIwz-E0G_5okH14g,6420
@@ -25,16 +29,16 @@ clapp-packages-repo/packages/test-app/main.py,sha256=rN4Zo9u53bIVjcUlul059knx6v-
25
29
  clapp-packages-repo/packages/test-app/manifest.json,sha256=kJe4sjYdPRNZD5hEeca80jj3lxeEWBMJoZ59RW7tiKI,118
26
30
  clapp-packages-repo/packages/test-app2/main.py,sha256=lHkbjTmehFY4VuYYF2dYiVBH7W0oqHHeY0I5W85iPTY,35
27
31
  clapp-packages-repo/packages/test-app2/manifest.json,sha256=vshXJrtRxBc_ISM6E8KT5BSmveMbjWszenlgxgSN86w,121
28
- clapp_pm-1.0.0.data/data/version.json,sha256=t-6ck4W0TVkQGg81ilCfG04OMivEbfsliJzbZudK3LY,238
29
- clapp_pm-1.0.0.dist-info/licenses/LICENSE,sha256=_hryv9pKR6udRexceUYuoYCJGmYBz7e-vRuFWmm38UY,1075
32
+ clapp_pm-1.0.2.data/data/version.json,sha256=S7yOmQU4A-UqpUE3AX0vmjehgSS--fxuVEPG1YcIvDo,238
33
+ clapp_pm-1.0.2.dist-info/licenses/LICENSE,sha256=_hryv9pKR6udRexceUYuoYCJGmYBz7e-vRuFWmm38UY,1075
30
34
  packages/hello-python/main.py,sha256=Dy-Ov-Vumj8oQYI6qKWU6fIKD0gCB8b7KzAJVrGyLMg,1429
31
35
  packages/hello-python/manifest.json,sha256=fJOVJk_2rwpRJ6IeWMPieklJD3gAR279jvuqRH69s90,179
32
36
  packages/test-app/main.py,sha256=rN4Zo9u53bIVjcUlul059knx6v-2Cd1MFftPS57FIRU,33
33
37
  packages/test-app/manifest.json,sha256=kJe4sjYdPRNZD5hEeca80jj3lxeEWBMJoZ59RW7tiKI,118
34
38
  packages/test-app2/main.py,sha256=lHkbjTmehFY4VuYYF2dYiVBH7W0oqHHeY0I5W85iPTY,35
35
39
  packages/test-app2/manifest.json,sha256=vshXJrtRxBc_ISM6E8KT5BSmveMbjWszenlgxgSN86w,121
36
- clapp_pm-1.0.0.dist-info/METADATA,sha256=1sbjynftkku1Zi4GyLV4j8LvImw5g5KvTNrkb8JXIjI,3979
37
- clapp_pm-1.0.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
- clapp_pm-1.0.0.dist-info/entry_points.txt,sha256=7j-3pQVpQfnaVzUV83g7zlCA30ePlnXkhHLAGGz9xrQ,36
39
- clapp_pm-1.0.0.dist-info/top_level.txt,sha256=dQhc-if0FtK86ImmFAUfXHm8hb6Y_ppQ6oMvD8tZ7ew,288
40
- clapp_pm-1.0.0.dist-info/RECORD,,
40
+ clapp_pm-1.0.2.dist-info/METADATA,sha256=Yqpr_xf6m8POnPt648jyOtC2wTLHNALOsY3nnI6uKSw,3979
41
+ clapp_pm-1.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
42
+ clapp_pm-1.0.2.dist-info/entry_points.txt,sha256=7j-3pQVpQfnaVzUV83g7zlCA30ePlnXkhHLAGGz9xrQ,36
43
+ clapp_pm-1.0.2.dist-info/top_level.txt,sha256=90PY3qqc7ZdaDsLvwHtQiUG0OZqpsjxw_zkjiTTFY0I,351
44
+ clapp_pm-1.0.2.dist-info/RECORD,,
@@ -6,7 +6,9 @@ cli_commands
6
6
  dependency_resolver
7
7
  doctor_command
8
8
  info_command
9
+ install_command
9
10
  installer
11
+ list_command
10
12
  main
11
13
  manifest_schema
12
14
  manifest_validator
@@ -14,7 +16,9 @@ package_registry
14
16
  package_runner
15
17
  packages
16
18
  post_install_hint
19
+ publish_command
17
20
  remote_registry
21
+ uninstall_command
18
22
  validate_command
19
23
  version_command
20
24
  where_command
doctor_command.py CHANGED
@@ -20,7 +20,6 @@ from check_env import (
20
20
  check_working_directory,
21
21
  check_apps_directory,
22
22
  check_permissions,
23
- check_flet_installation
24
23
  )
25
24
 
26
25
  def check_clapp_config():
@@ -165,13 +164,25 @@ def check_python_modules():
165
164
  else:
166
165
  return True, f"Tüm gerekli modüller mevcut ({len(found)} modül)"
167
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
+
168
180
  def run_doctor():
169
181
  """Kapsamlı sistem tanılaması yapar"""
170
182
  print("🩺 clapp Sistem Tanılaması")
171
183
  print("=" * 60)
172
184
  print("Sisteminiz clapp için uygun mu kontrol ediliyor...")
173
185
  print()
174
-
175
186
  # Tüm kontroller
176
187
  checks = [
177
188
  ("Python Sürümü", check_python_version),
@@ -187,93 +198,42 @@ def run_doctor():
187
198
  ("Yazma İzinleri", check_permissions),
188
199
  ("Disk Alanı", check_disk_space),
189
200
  ("Ağ Erişimi", check_network_access),
190
- ("Flet Kurulumu", check_flet_installation),
191
201
  ("clapp Konfigürasyonu", check_clapp_config),
192
202
  ]
193
-
194
203
  passed = 0
195
204
  failed = 0
196
205
  warnings = 0
197
-
198
206
  results = []
199
-
200
207
  for check_name, check_func in checks:
201
208
  try:
202
- success, message = check_func()
203
- results.append((check_name, success, message))
204
-
205
- if success:
206
- print(f"✅ {check_name}: {message}")
209
+ ok, msg = check_func()
210
+ if ok:
211
+ print(f"✅ {check_name}: {msg}")
207
212
  passed += 1
208
213
  else:
209
- print(f"❌ {check_name}: {message}")
214
+ print(f"❌ {check_name}: {msg}")
210
215
  failed += 1
216
+ results.append((check_name, ok, msg))
211
217
  except Exception as e:
212
- error_msg = f"Hata - {str(e)}"
213
- results.append((check_name, False, error_msg))
214
- print(f"⚠️ {check_name}: {error_msg}")
215
- warnings += 1
216
-
217
- # Özet
218
+ print(f" {check_name}: Kontrol sırasında hata: {e}")
219
+ failed += 1
220
+ results.append((check_name, False, str(e)))
218
221
  print("\n" + "=" * 60)
219
- print("📊 Tanılama Özeti:")
222
+ print(f"📊 Tanılama Özeti:")
220
223
  print(f"✅ Başarılı: {passed}")
221
224
  print(f"❌ Başarısız: {failed}")
222
225
  print(f"⚠️ Uyarı: {warnings}")
223
-
224
- # Genel durum
225
- if failed == 0 and warnings == 0:
226
- print("\n🎉 Mükemmel! Sisteminiz clapp için tamamen hazır.")
227
- print("✨ Herhangi bir sorun bulunmadı.")
228
- elif failed == 0:
229
- print("\n✅ İyi! Sisteminiz clapp için genel olarak uygun.")
230
- print("⚠️ Bazı küçük uyarılar var, ancak çalışmaya engel değil.")
231
- else:
226
+ if failed > 0:
232
227
  print("\n🔧 Dikkat! Bazı sorunlar bulundu.")
233
228
  print("❌ Aşağıdaki sorunları çözmeniz önerilir:")
234
-
235
- # Detaylı öneriler
236
- if failed > 0:
237
- print("\n💡 Çözüm Önerileri:")
238
-
239
- for check_name, success, message in results:
240
- if not success:
241
- print(f"\n🔧 {check_name}:")
242
- print(f" Sorun: {message}")
243
-
244
- # Spesifik öneriler
245
- if "Python" in check_name and "sürüm" in message.lower():
246
- print(" Çözüm: Python 3.8 veya daha yeni sürüm yükleyin")
247
- elif "PATH" in check_name:
248
- print(" Çözüm: Python Scripts dizinini PATH'e ekleyin")
249
- print(" Detay: clapp check-env komutunu çalıştırın")
250
- elif "apps/" in check_name:
251
- print(" Çözüm: mkdir apps komutu ile apps dizini oluşturun")
252
- elif "Flet" in check_name:
253
- print(" Çözüm: pip install flet komutu ile Flet'i yükleyin")
254
- elif "izin" in message.lower():
255
- print(" Çözüm: Dizin izinlerini kontrol edin veya farklı dizinde çalıştırın")
256
- elif "disk" in message.lower():
257
- print(" Çözüm: Disk alanı açın veya farklı dizinde çalıştırın")
258
- elif "ağ" in message.lower():
259
- print(" Çözüm: İnternet bağlantınızı kontrol edin")
260
-
261
- # Sonraki adımlar
262
- print("\n🚀 Sonraki Adımlar:")
263
- if failed == 0:
264
- print("• clapp list - Yüklü uygulamaları listeleyin")
265
- print("• clapp gui - Grafik arayüzü başlatın")
266
- print("• clapp --help - Tüm komutları görün")
229
+ for name, ok, msg in results:
230
+ if not ok:
231
+ print(f"\n🔧 {name}:\n Sorun: {msg}")
267
232
  else:
268
- print(" Yukarıdaki sorunları çözün")
269
- print("• clapp doctor - Tekrar tanılama çalıştırın")
270
- print("• clapp check-env - Temel kontrolleri yapın")
271
-
233
+ print("\n🚀 Her şey yolunda! clapp sorunsuz çalışabilir.")
272
234
  print("\n📞 Yardım:")
273
235
  print("• GitHub: https://github.com/user/clapp")
274
236
  print("• Dokümantasyon: README.md dosyasını okuyun")
275
-
276
- return failed == 0
277
237
 
278
238
  if __name__ == "__main__":
279
239
  run_doctor()
install_command.py ADDED
@@ -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()
installer.py CHANGED
@@ -7,81 +7,84 @@ import json
7
7
  from manifest_validator import validate_manifest_verbose
8
8
  from package_registry import app_exists
9
9
 
10
+ def find_app_folder(extract_path, app_name):
11
+ """
12
+ Zip çıkarıldıktan sonra, extract_path altında **/packages/{app_name} klasörünü bulur.
13
+ """
14
+ for root, dirs, files in os.walk(extract_path):
15
+ if os.path.basename(root) == app_name and os.path.basename(os.path.dirname(root)) == "packages":
16
+ return root
17
+ return None
18
+
19
+
10
20
  def install_package(source, force=False):
11
21
  """
12
22
  Bir .clapp paketini zip dosyasından veya URL'den yükler.
13
-
14
- Args:
15
- source (str): Yerel zip dosya yolu veya uzak URL
16
- force (bool): Mevcut uygulamanın üzerine yazılmasına izin ver
17
-
18
- Returns:
19
- tuple: (success: bool, message: str)
20
23
  """
21
24
  temp_dir = None
22
-
23
25
  try:
24
- # Geçici dizin oluştur
25
26
  temp_dir = tempfile.mkdtemp()
26
-
27
- # Kaynak türünü belirle (URL mu dosya yolu mu)
28
27
  if source.startswith(('http://', 'https://')):
29
- # URL'den indir
30
28
  zip_path = download_package(source, temp_dir)
31
29
  if not zip_path:
32
30
  return False, "Paket indirilemedi"
33
31
  else:
34
- # Yerel dosya
35
32
  if not os.path.exists(source):
36
33
  return False, f"Dosya bulunamadı: {source}"
37
34
  zip_path = source
38
-
39
- # Zip dosyasını çıkart
40
35
  extract_path = os.path.join(temp_dir, "extracted")
41
36
  success, message = extract_package(zip_path, extract_path)
42
37
  if not success:
43
38
  return False, message
44
-
45
- # Manifest'i yükle ve doğrula
46
- manifest_path = os.path.join(extract_path, "manifest.json")
47
- is_valid, errors = validate_manifest_file(manifest_path)
39
+
40
+ # --- YENİ: Doğru app klasörünü bul ---
41
+ # Önce manifesti bulmak için tüm app klasörlerini tara
42
+ app_folder = None
43
+ manifest = None
44
+ manifest_path = None
45
+ # Tüm packages altındaki app klasörlerini bul
46
+ for root, dirs, files in os.walk(extract_path):
47
+ if "manifest.json" in files:
48
+ with open(os.path.join(root, "manifest.json"), 'r', encoding='utf-8') as f:
49
+ try:
50
+ m = json.load(f)
51
+ if 'name' in m:
52
+ app_folder = root
53
+ manifest = m
54
+ manifest_path = os.path.join(root, "manifest.json")
55
+ break
56
+ except Exception:
57
+ continue
58
+ if not app_folder or not manifest:
59
+ return False, "Uygulama klasörü bulunamadı: manifest.json"
60
+ app_name = manifest['name']
61
+
62
+ # --- YENİ: Sadece packages/{app_name} klasörünü bul ve kopyala ---
63
+ app_real_folder = find_app_folder(extract_path, app_name)
64
+ if not app_real_folder:
65
+ return False, f"Uygulama klasörü bulunamadı: packages/{app_name}"
66
+
67
+ # Manifesti doğrula
68
+ is_valid, errors = validate_manifest_file(os.path.join(app_real_folder, "manifest.json"))
48
69
  if not is_valid:
49
70
  error_msg = "Manifest doğrulama hatası:\n" + "\n".join(errors)
50
71
  return False, error_msg
51
-
52
- # Manifest'i yükle
53
- with open(manifest_path, 'r', encoding='utf-8') as f:
54
- manifest = json.load(f)
55
-
56
- app_name = manifest['name']
57
-
58
- # Uygulama zaten var mı kontrol et
59
- if app_exists(app_name) and not force:
60
- return False, f"Uygulama '{app_name}' zaten yüklü. --force kullanarak üzerine yazabilirsiniz."
61
-
72
+
62
73
  # Giriş dosyasının varlığını kontrol et
63
74
  entry_file = manifest['entry']
64
- entry_path = os.path.join(extract_path, entry_file)
75
+ entry_path = os.path.join(app_real_folder, entry_file)
65
76
  if not os.path.exists(entry_path):
66
77
  return False, f"Giriş dosyası bulunamadı: {entry_file}"
67
-
78
+
68
79
  # Hedef dizini oluştur
69
80
  target_dir = os.path.join("apps", app_name)
70
-
71
- # Mevcut dizini sil (eğer varsa)
72
81
  if os.path.exists(target_dir):
73
82
  shutil.rmtree(target_dir)
74
-
75
- # Dosyaları kopyala
76
- shutil.copytree(extract_path, target_dir)
77
-
83
+ shutil.copytree(app_real_folder, target_dir)
78
84
  return True, f"✅ '{app_name}' başarıyla yüklendi!"
79
-
80
85
  except Exception as e:
81
86
  return False, f"Yükleme hatası: {e}"
82
-
83
87
  finally:
84
- # Geçici dizini temizle
85
88
  if temp_dir and os.path.exists(temp_dir):
86
89
  shutil.rmtree(temp_dir)
87
90
 
list_command.py ADDED
@@ -0,0 +1,262 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ list_command.py - clapp List Command
4
+
5
+ Bu modül 'clapp list' komutunu uygular.
6
+ Kurulu uygulamaları farklı formatlarda listeler.
7
+ """
8
+
9
+ import os
10
+ import json
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import List, Dict, Any, Optional
14
+
15
+ def get_apps_directory() -> str:
16
+ """Uygulamaların kurulu olduğu dizini döndürür"""
17
+ # Kullanıcının home dizininde .clapp klasörü
18
+ home_dir = Path.home()
19
+ clapp_dir = home_dir / ".clapp"
20
+ apps_dir = clapp_dir / "apps"
21
+
22
+ return str(apps_dir)
23
+
24
+ def load_app_manifest(app_path: str) -> Optional[Dict[str, Any]]:
25
+ """Uygulama manifest'ini yükler"""
26
+ manifest_path = os.path.join(app_path, "manifest.json")
27
+
28
+ if not os.path.exists(manifest_path):
29
+ return None
30
+
31
+ try:
32
+ with open(manifest_path, 'r', encoding='utf-8') as f:
33
+ return json.load(f)
34
+ except Exception:
35
+ return None
36
+
37
+ def get_installed_apps_with_info() -> List[Dict[str, Any]]:
38
+ """Kurulu uygulamaların detaylı bilgilerini döndürür"""
39
+ apps_dir = get_apps_directory()
40
+ apps = []
41
+
42
+ if not os.path.exists(apps_dir):
43
+ return apps
44
+
45
+ for item in os.listdir(apps_dir):
46
+ item_path = os.path.join(apps_dir, item)
47
+
48
+ if not os.path.isdir(item_path) or item.startswith('.'):
49
+ continue
50
+
51
+ manifest = load_app_manifest(item_path)
52
+
53
+ if manifest:
54
+ app_info = {
55
+ 'name': manifest.get('name', item),
56
+ 'version': manifest.get('version', '?'),
57
+ 'language': manifest.get('language', '?'),
58
+ 'description': manifest.get('description', 'Açıklama yok'),
59
+ 'entry': manifest.get('entry', '?'),
60
+ 'dependencies': manifest.get('dependencies', []),
61
+ 'folder': item,
62
+ 'path': item_path
63
+ }
64
+ else:
65
+ app_info = {
66
+ 'name': item,
67
+ 'version': '?',
68
+ 'language': '?',
69
+ 'description': 'Manifest bulunamadı',
70
+ 'entry': '?',
71
+ 'dependencies': [],
72
+ 'folder': item,
73
+ 'path': item_path
74
+ }
75
+
76
+ apps.append(app_info)
77
+
78
+ return apps
79
+
80
+ def format_table_output(apps: List[Dict[str, Any]]) -> str:
81
+ """Uygulamaları tablo formatında formatlar"""
82
+ if not apps:
83
+ return "📭 Hiç uygulama kurulu değil."
84
+
85
+ # Başlık
86
+ output = []
87
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)})")
88
+ output.append("=" * 80)
89
+
90
+ # Sütun başlıkları
91
+ output.append(f"{'Ad':<20} {'Sürüm':<10} {'Dil':<10} {'Açıklama':<30}")
92
+ output.append("-" * 80)
93
+
94
+ # Uygulamalar
95
+ for app in sorted(apps, key=lambda x: x['name'].lower()):
96
+ name = app['name'][:19]
97
+ version = app['version'][:9]
98
+ language = app['language'][:9]
99
+ description = app['description'][:29]
100
+
101
+ output.append(f"{name:<20} {version:<10} {language:<10} {description:<30}")
102
+
103
+ return "\n".join(output)
104
+
105
+ def format_simple_output(apps: List[Dict[str, Any]]) -> str:
106
+ """Uygulamaları basit liste formatında formatlar"""
107
+ if not apps:
108
+ return "📭 Hiç uygulama kurulu değil."
109
+
110
+ output = []
111
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)}):")
112
+
113
+ for app in sorted(apps, key=lambda x: x['name'].lower()):
114
+ name = app['name']
115
+ version = app['version']
116
+ language = app['language']
117
+ output.append(f" • {name} (v{version}) - {language}")
118
+
119
+ return "\n".join(output)
120
+
121
+ def format_json_output(apps: List[Dict[str, Any]]) -> str:
122
+ """Uygulamaları JSON formatında formatlar"""
123
+ return json.dumps(apps, indent=2, ensure_ascii=False)
124
+
125
+ def format_detailed_output(apps: List[Dict[str, Any]]) -> str:
126
+ """Uygulamaları detaylı formatda formatlar"""
127
+ if not apps:
128
+ return "📭 Hiç uygulama kurulu değil."
129
+
130
+ output = []
131
+ output.append(f"📦 Kurulu Uygulamalar ({len(apps)})")
132
+ output.append("=" * 60)
133
+
134
+ for i, app in enumerate(sorted(apps, key=lambda x: x['name'].lower()), 1):
135
+ output.append(f"\n{i}. {app['name']}")
136
+ output.append(f" Sürüm: {app['version']}")
137
+ output.append(f" Dil: {app['language']}")
138
+ output.append(f" Açıklama: {app['description']}")
139
+ output.append(f" Entry: {app['entry']}")
140
+ output.append(f" Klasör: {app['folder']}")
141
+ output.append(f" Yol: {app['path']}")
142
+
143
+ if app['dependencies']:
144
+ output.append(f" Bağımlılıklar: {', '.join(app['dependencies'])}")
145
+ else:
146
+ output.append(f" Bağımlılıklar: Yok")
147
+
148
+ return "\n".join(output)
149
+
150
+ def filter_apps_by_language(apps: List[Dict[str, Any]], language: str) -> List[Dict[str, Any]]:
151
+ """Uygulamaları dile göre filtreler"""
152
+ return [app for app in apps if app['language'].lower() == language.lower()]
153
+
154
+ def filter_apps_by_name(apps: List[Dict[str, Any]], search_term: str) -> List[Dict[str, Any]]:
155
+ """Uygulamaları isme göre filtreler"""
156
+ search_term = search_term.lower()
157
+ return [app for app in apps if search_term in app['name'].lower() or search_term in app['description'].lower()]
158
+
159
+ def list_apps(format_type: str = "table", language_filter: str = None, search_term: str = None) -> str:
160
+ """
161
+ Ana list fonksiyonu
162
+
163
+ Args:
164
+ format_type: Çıktı formatı (table, simple, json, detailed)
165
+ language_filter: Dil filtresi (python, lua, vb.)
166
+ search_term: Arama terimi
167
+
168
+ Returns:
169
+ Formatlanmış çıktı
170
+ """
171
+ apps = get_installed_apps_with_info()
172
+
173
+ # Filtreleri uygula
174
+ if language_filter:
175
+ apps = filter_apps_by_language(apps, language_filter)
176
+
177
+ if search_term:
178
+ apps = filter_apps_by_name(apps, search_term)
179
+
180
+ # Formatı uygula
181
+ if format_type == "json":
182
+ return format_json_output(apps)
183
+ elif format_type == "simple":
184
+ return format_simple_output(apps)
185
+ elif format_type == "detailed":
186
+ return format_detailed_output(apps)
187
+ else: # table (default)
188
+ return format_table_output(apps)
189
+
190
+ def show_help():
191
+ """Yardım mesajını gösterir"""
192
+ help_text = """
193
+ clapp list - Kurulu uygulamaları listeler
194
+
195
+ Kullanım:
196
+ python list_command.py [seçenekler]
197
+
198
+ Seçenekler:
199
+ --format FORMAT Çıktı formatı (table, simple, json, detailed)
200
+ --language LANG Dil filtresi (python, lua, vb.)
201
+ --search TERM Arama terimi (ad veya açıklamada)
202
+ --help Bu yardım mesajını göster
203
+
204
+ Örnekler:
205
+ python list_command.py
206
+ python list_command.py --format json
207
+ python list_command.py --format simple
208
+ python list_command.py --format detailed
209
+ python list_command.py --language python
210
+ python list_command.py --search calculator
211
+ python list_command.py --language python --format detailed
212
+ """
213
+ print(help_text.strip())
214
+
215
+ def main():
216
+ """CLI entry point"""
217
+ # Komut satırı argümanlarını parse et
218
+ args = sys.argv[1:]
219
+
220
+ if "--help" in args or "-h" in args:
221
+ show_help()
222
+ sys.exit(0)
223
+
224
+ format_type = "table"
225
+ language_filter = None
226
+ search_term = None
227
+
228
+ i = 0
229
+ while i < len(args):
230
+ arg = args[i]
231
+
232
+ if arg == "--format" and i + 1 < len(args):
233
+ format_type = args[i + 1]
234
+ i += 2
235
+ elif arg == "--language" and i + 1 < len(args):
236
+ language_filter = args[i + 1]
237
+ i += 2
238
+ elif arg == "--search" and i + 1 < len(args):
239
+ search_term = args[i + 1]
240
+ i += 2
241
+ else:
242
+ print(f"❌ Bilinmeyen argüman: {arg}")
243
+ print("Yardım için: python list_command.py --help")
244
+ sys.exit(1)
245
+
246
+ # Format kontrolü
247
+ valid_formats = ["table", "simple", "json", "detailed"]
248
+ if format_type not in valid_formats:
249
+ print(f"❌ Geçersiz format: {format_type}")
250
+ print(f"Geçerli formatlar: {', '.join(valid_formats)}")
251
+ sys.exit(1)
252
+
253
+ # Uygulamaları listele
254
+ try:
255
+ output = list_apps(format_type, language_filter, search_term)
256
+ print(output)
257
+ except Exception as e:
258
+ print(f"❌ Hata: {e}")
259
+ sys.exit(1)
260
+
261
+ if __name__ == "__main__":
262
+ main()
publish_command.py ADDED
@@ -0,0 +1,253 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ publish_command.py - clapp Publish Command
4
+
5
+ Bu modül 'clapp publish <folder>' komutunu uygular.
6
+ Bir uygulama klasörünü validate edip packages/ klasörüne kopyalar
7
+ ve index.json'u günceller. Opsiyonel olarak clapp-packages reposuna push eder.
8
+ """
9
+
10
+ import os
11
+ import shutil
12
+ import subprocess
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import Tuple, Optional
16
+
17
+ from manifest_validator import validate_manifest_verbose
18
+ from manifest_schema import load_manifest
19
+
20
+ def validate_app_folder(folder_path: str) -> Tuple[bool, str, Optional[dict]]:
21
+ """
22
+ Uygulama klasörünü doğrular
23
+
24
+ Returns:
25
+ (success, message, manifest_data)
26
+ """
27
+ if not os.path.exists(folder_path):
28
+ return False, f"Klasör bulunamadı: {folder_path}", None
29
+
30
+ if not os.path.isdir(folder_path):
31
+ return False, f"Geçerli bir klasör değil: {folder_path}", None
32
+
33
+ # manifest.json kontrolü
34
+ manifest_path = os.path.join(folder_path, "manifest.json")
35
+ if not os.path.exists(manifest_path):
36
+ return False, "manifest.json dosyası bulunamadı", None
37
+
38
+ try:
39
+ manifest = load_manifest(manifest_path)
40
+ except Exception as e:
41
+ return False, f"manifest.json okunamadı: {e}", None
42
+
43
+ # Manifest doğrulama
44
+ is_valid, errors = validate_manifest_verbose(manifest)
45
+ if not is_valid:
46
+ error_msg = "Manifest doğrulama hatası:\n" + "\n".join(f" - {error}" for error in errors)
47
+ return False, error_msg, None
48
+
49
+ # Entry file kontrolü
50
+ entry_file = manifest.get('entry')
51
+ if entry_file:
52
+ entry_path = os.path.join(folder_path, entry_file)
53
+ if not os.path.exists(entry_path):
54
+ return False, f"Entry dosyası bulunamadı: {entry_file}", None
55
+
56
+ return True, "Doğrulama başarılı", manifest
57
+
58
+ def copy_app_to_packages(source_folder: str, app_name: str) -> Tuple[bool, str]:
59
+ """
60
+ Uygulama klasörünü packages/ altına kopyalar
61
+
62
+ Returns:
63
+ (success, message)
64
+ """
65
+ try:
66
+ packages_dir = "./packages"
67
+ target_path = os.path.join(packages_dir, app_name)
68
+
69
+ # packages klasörünü oluştur
70
+ os.makedirs(packages_dir, exist_ok=True)
71
+
72
+ # Eğer hedef klasör varsa, sil
73
+ if os.path.exists(target_path):
74
+ shutil.rmtree(target_path)
75
+ print(f"⚠️ Mevcut {app_name} klasörü silindi")
76
+
77
+ # Kopyala
78
+ shutil.copytree(source_folder, target_path)
79
+ print(f"✅ {app_name} -> packages/{app_name} kopyalandı")
80
+
81
+ return True, f"Uygulama başarıyla kopyalandı: packages/{app_name}"
82
+
83
+ except Exception as e:
84
+ return False, f"Kopyalama hatası: {e}"
85
+
86
+ def update_index() -> Tuple[bool, str]:
87
+ """
88
+ build_index.py script'ini çalıştırarak index.json'u günceller
89
+
90
+ Returns:
91
+ (success, message)
92
+ """
93
+ try:
94
+ # build_index.py'yi çalıştır
95
+ result = subprocess.run([
96
+ sys.executable, "build_index.py"
97
+ ], capture_output=True, text=True, cwd=".")
98
+
99
+ if result.returncode == 0:
100
+ return True, "Index başarıyla güncellendi"
101
+ else:
102
+ return False, f"Index güncelleme hatası: {result.stderr}"
103
+
104
+ except Exception as e:
105
+ return False, f"Index script çalıştırılamadı: {e}"
106
+
107
+ def push_to_clapp_packages_repo(app_name: str, app_version: str) -> Tuple[bool, str]:
108
+ """
109
+ Değişiklikleri clapp-packages reposuna push eder
110
+
111
+ Returns:
112
+ (success, message)
113
+ """
114
+ try:
115
+ print("4️⃣ clapp-packages reposuna push ediliyor...")
116
+
117
+ # clapp-packages reposunu kontrol et
118
+ packages_repo_path = "./clapp-packages-repo"
119
+
120
+ # Eğer clapp-packages repo klonlanmamışsa, klonla
121
+ if not os.path.exists(packages_repo_path):
122
+ print("📥 clapp-packages reposu klonlanıyor...")
123
+ subprocess.run([
124
+ 'git', 'clone', 'https://github.com/mburakmmm/clapp-packages.git',
125
+ packages_repo_path
126
+ ], check=True, cwd=".")
127
+
128
+ # packages/ klasörünü clapp-packages reposuna kopyala
129
+ source_packages = "./packages"
130
+ target_packages = os.path.join(packages_repo_path, "packages")
131
+
132
+ if os.path.exists(target_packages):
133
+ shutil.rmtree(target_packages)
134
+
135
+ shutil.copytree(source_packages, target_packages)
136
+ print(f"✅ packages/ klasörü clapp-packages reposuna kopyalandı")
137
+
138
+ # index.json'u da kopyala
139
+ if os.path.exists("index.json"):
140
+ shutil.copy("index.json", os.path.join(packages_repo_path, "index.json"))
141
+ print("✅ index.json clapp-packages reposuna kopyalandı")
142
+
143
+ # clapp-packages reposuna git işlemleri
144
+ os.chdir(packages_repo_path)
145
+
146
+ # Git durumunu kontrol et
147
+ result = subprocess.run(['git', 'status', '--porcelain'],
148
+ capture_output=True, text=True)
149
+
150
+ if not result.stdout.strip():
151
+ os.chdir("..")
152
+ return True, "Değişiklik yok, push gerekmiyor"
153
+
154
+ # Değişiklikleri ekle
155
+ subprocess.run(['git', 'add', '.'], check=True)
156
+
157
+ # Commit oluştur
158
+ commit_message = f"📦 Publish {app_name} v{app_version}\n\n- {app_name} uygulaması packages/ klasörüne eklendi\n- index.json güncellendi\n- Otomatik publish işlemi"
159
+
160
+ subprocess.run(['git', 'commit', '-m', commit_message], check=True)
161
+
162
+ # Push et
163
+ subprocess.run(['git', 'push', 'origin', 'main'], check=True)
164
+
165
+ # Ana dizine geri dön
166
+ os.chdir("..")
167
+
168
+ return True, "clapp-packages reposuna başarıyla push edildi"
169
+
170
+ except subprocess.CalledProcessError as e:
171
+ # Ana dizine geri dön
172
+ if os.getcwd() != os.path.abspath("."):
173
+ os.chdir("..")
174
+ return False, f"Git işlemi hatası: {e}"
175
+ except Exception as e:
176
+ # Ana dizine geri dön
177
+ if os.getcwd() != os.path.abspath("."):
178
+ os.chdir("..")
179
+ return False, f"Push hatası: {e}"
180
+
181
+ def publish_app(folder_path: str, force: bool = False, push_to_github: bool = False) -> Tuple[bool, str]:
182
+ """
183
+ Ana publish fonksiyonu
184
+
185
+ Args:
186
+ folder_path: Publish edilecek uygulama klasörü
187
+ force: Zorla üzerine yaz
188
+ push_to_github: clapp-packages reposuna push et
189
+
190
+ Returns:
191
+ (success, message)
192
+ """
193
+ print(f"🚀 Publish başlatılıyor: {folder_path}")
194
+ print("=" * 50)
195
+
196
+ # 1. Klasörü doğrula
197
+ print("1️⃣ Uygulama doğrulanıyor...")
198
+ is_valid, message, manifest = validate_app_folder(folder_path)
199
+
200
+ if not is_valid:
201
+ return False, f"Doğrulama hatası: {message}"
202
+
203
+ app_name = manifest['name']
204
+ app_version = manifest['version']
205
+ print(f"✅ {app_name} v{app_version} doğrulandı")
206
+
207
+ # 2. Packages klasörüne kopyala
208
+ print("2️⃣ Uygulama kopyalanıyor...")
209
+ copy_success, copy_message = copy_app_to_packages(folder_path, app_name)
210
+
211
+ if not copy_success:
212
+ return False, copy_message
213
+
214
+ # 3. Index'i güncelle
215
+ print("3️⃣ Index güncelleniyor...")
216
+ index_success, index_message = update_index()
217
+
218
+ if not index_success:
219
+ return False, index_message
220
+
221
+ # 4. clapp-packages reposuna push (opsiyonel)
222
+ if push_to_github:
223
+ push_success, push_message = push_to_clapp_packages_repo(app_name, app_version)
224
+ if not push_success:
225
+ print(f"⚠️ {push_message}")
226
+ return True, f"🎉 '{app_name}' yerel olarak publish edildi! clapp-packages push başarısız."
227
+
228
+ return True, f"🎉 '{app_name}' başarıyla publish edildi! Index güncellendi."
229
+
230
+ def main():
231
+ """CLI entry point"""
232
+ if len(sys.argv) < 2:
233
+ print("Kullanım: python publish_command.py <folder_path> [--push]")
234
+ print("Örnek: python publish_command.py ./my-app")
235
+ print("Örnek: python publish_command.py ./my-app --push")
236
+ sys.exit(1)
237
+
238
+ folder_path = sys.argv[1]
239
+ force = "--force" in sys.argv
240
+ push_to_github = "--push" in sys.argv
241
+
242
+ success, message = publish_app(folder_path, force, push_to_github)
243
+
244
+ print("\n" + "=" * 50)
245
+ if success:
246
+ print(f"✅ {message}")
247
+ sys.exit(0)
248
+ else:
249
+ print(f"❌ {message}")
250
+ sys.exit(1)
251
+
252
+ if __name__ == "__main__":
253
+ main()
uninstall_command.py ADDED
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ uninstall_command.py - clapp Uninstall Command
4
+
5
+ Bu modül 'clapp uninstall <app_name>' komutunu uygular.
6
+ Kurulu uygulamaları güvenli bir şekilde kaldırır.
7
+ """
8
+
9
+ import os
10
+ import shutil
11
+ import sys
12
+ from pathlib import Path
13
+ from typing import Tuple, List
14
+
15
+ def get_apps_directory() -> str:
16
+ """Uygulamaların kurulu olduğu dizini döndürür"""
17
+ # Kullanıcının home dizininde .clapp klasörü
18
+ home_dir = Path.home()
19
+ clapp_dir = home_dir / ".clapp"
20
+ apps_dir = clapp_dir / "apps"
21
+
22
+ return str(apps_dir)
23
+
24
+ def get_installed_apps() -> List[str]:
25
+ """Kurulu uygulamaların listesini döndürür"""
26
+ apps_dir = get_apps_directory()
27
+
28
+ if not os.path.exists(apps_dir):
29
+ return []
30
+
31
+ apps = []
32
+ for item in os.listdir(apps_dir):
33
+ item_path = os.path.join(apps_dir, item)
34
+ if os.path.isdir(item_path) and not item.startswith('.'):
35
+ # manifest.json varlığını kontrol et
36
+ manifest_path = os.path.join(item_path, "manifest.json")
37
+ if os.path.exists(manifest_path):
38
+ apps.append(item)
39
+
40
+ return apps
41
+
42
+ def is_app_installed(app_name: str) -> bool:
43
+ """Uygulamanın kurulu olup olmadığını kontrol eder"""
44
+ apps_dir = get_apps_directory()
45
+ app_path = os.path.join(apps_dir, app_name)
46
+
47
+ if not os.path.exists(app_path):
48
+ return False
49
+
50
+ if not os.path.isdir(app_path):
51
+ return False
52
+
53
+ # manifest.json varlığını kontrol et
54
+ manifest_path = os.path.join(app_path, "manifest.json")
55
+ return os.path.exists(manifest_path)
56
+
57
+ def get_app_info(app_name: str) -> Tuple[bool, str, dict]:
58
+ """Uygulamanın bilgilerini getirir"""
59
+ apps_dir = get_apps_directory()
60
+ app_path = os.path.join(apps_dir, app_name)
61
+ manifest_path = os.path.join(app_path, "manifest.json")
62
+
63
+ try:
64
+ import json
65
+ with open(manifest_path, 'r', encoding='utf-8') as f:
66
+ manifest = json.load(f)
67
+
68
+ return True, "Bilgiler alındı", manifest
69
+ except Exception as e:
70
+ return False, f"Manifest okunamadı: {e}", {}
71
+
72
+ def confirm_uninstall(app_name: str, app_info: dict, skip_confirmation: bool = False) -> bool:
73
+ """Kullanıcıdan kaldırma onayı alır"""
74
+ if skip_confirmation:
75
+ return True
76
+
77
+ print(f"\n📋 Kaldırılacak uygulama:")
78
+ print(f" Ad: {app_info.get('name', app_name)}")
79
+ print(f" Sürüm: {app_info.get('version', 'Bilinmiyor')}")
80
+ print(f" Dil: {app_info.get('language', 'Bilinmiyor')}")
81
+ print(f" Açıklama: {app_info.get('description', 'Yok')}")
82
+
83
+ while True:
84
+ response = input(f"\n❓ '{app_name}' uygulamasını kaldırmak istediğinizden emin misiniz? (e/h): ").lower().strip()
85
+ if response in ['e', 'evet', 'y', 'yes']:
86
+ return True
87
+ elif response in ['h', 'hayır', 'n', 'no']:
88
+ return False
89
+ else:
90
+ print("Lütfen 'e' (evet) veya 'h' (hayır) giriniz.")
91
+
92
+ def remove_app_directory(app_name: str) -> Tuple[bool, str]:
93
+ """Uygulama klasörünü güvenli şekilde kaldırır"""
94
+ try:
95
+ apps_dir = get_apps_directory()
96
+ app_path = os.path.join(apps_dir, app_name)
97
+
98
+ # Güvenlik kontrolü - sadece .clapp/apps altındaki klasörleri sil
99
+ if not app_path.startswith(apps_dir):
100
+ return False, "Güvenlik hatası: Geçersiz klasör yolu"
101
+
102
+ if not os.path.exists(app_path):
103
+ return False, "Uygulama klasörü bulunamadı"
104
+
105
+ # Klasörü sil
106
+ shutil.rmtree(app_path)
107
+
108
+ return True, f"Uygulama klasörü kaldırıldı: {app_path}"
109
+
110
+ except PermissionError:
111
+ return False, "İzin hatası: Klasör kaldırılamadı"
112
+ except Exception as e:
113
+ return False, f"Kaldırma hatası: {e}"
114
+
115
+ def uninstall_app(app_name: str, skip_confirmation: bool = False) -> Tuple[bool, str]:
116
+ """
117
+ Ana uninstall fonksiyonu
118
+
119
+ Args:
120
+ app_name: Kaldırılacak uygulamanın adı
121
+ skip_confirmation: Onay sorma (--yes flag için)
122
+
123
+ Returns:
124
+ (success, message)
125
+ """
126
+ print(f"🗑️ Kaldırma başlatılıyor: {app_name}")
127
+ print("=" * 50)
128
+
129
+ # 1. Uygulama kurulu mu kontrol et
130
+ print("1️⃣ Uygulama kontrol ediliyor...")
131
+
132
+ if not is_app_installed(app_name):
133
+ installed_apps = get_installed_apps()
134
+ if installed_apps:
135
+ return False, f"Uygulama kurulu değil: {app_name}\nKurulu uygulamalar: {', '.join(installed_apps)}"
136
+ else:
137
+ return False, f"Uygulama kurulu değil: {app_name}\nHiç uygulama kurulu değil."
138
+
139
+ print(f"✅ {app_name} kurulu")
140
+
141
+ # 2. Uygulama bilgilerini al
142
+ print("2️⃣ Uygulama bilgileri alınıyor...")
143
+ info_success, info_message, app_info = get_app_info(app_name)
144
+
145
+ if not info_success:
146
+ print(f"⚠️ {info_message}")
147
+ app_info = {'name': app_name}
148
+
149
+ # 3. Kullanıcı onayı
150
+ if not skip_confirmation:
151
+ print("3️⃣ Kullanıcı onayı bekleniyor...")
152
+ if not confirm_uninstall(app_name, app_info, skip_confirmation):
153
+ return False, "Kaldırma işlemi iptal edildi"
154
+
155
+ # 4. Uygulamayı kaldır
156
+ print("4️⃣ Uygulama kaldırılıyor...")
157
+ remove_success, remove_message = remove_app_directory(app_name)
158
+
159
+ if not remove_success:
160
+ return False, remove_message
161
+
162
+ return True, f"🎉 '{app_name}' başarıyla kaldırıldı!"
163
+
164
+ def list_installed_apps():
165
+ """Kurulu uygulamaları listeler"""
166
+ apps = get_installed_apps()
167
+
168
+ if not apps:
169
+ print("📭 Hiç uygulama kurulu değil.")
170
+ return
171
+
172
+ print(f"📦 Kurulu uygulamalar ({len(apps)}):")
173
+ print("-" * 30)
174
+
175
+ for app_name in sorted(apps):
176
+ info_success, _, app_info = get_app_info(app_name)
177
+ if info_success:
178
+ version = app_info.get('version', '?')
179
+ language = app_info.get('language', '?')
180
+ print(f" • {app_name} (v{version}) - {language}")
181
+ else:
182
+ print(f" • {app_name} - bilgi alınamadı")
183
+
184
+ def main():
185
+ """CLI entry point"""
186
+ if len(sys.argv) < 2:
187
+ print("Kullanım: python uninstall_command.py <app_name> [--yes]")
188
+ print(" python uninstall_command.py --list")
189
+ print()
190
+ print("Örnekler:")
191
+ print(" python uninstall_command.py hello-python")
192
+ print(" python uninstall_command.py hello-python --yes")
193
+ print(" python uninstall_command.py --list")
194
+ sys.exit(1)
195
+
196
+ # --list flag kontrolü
197
+ if sys.argv[1] == "--list":
198
+ list_installed_apps()
199
+ sys.exit(0)
200
+
201
+ app_name = sys.argv[1]
202
+ skip_confirmation = "--yes" in sys.argv
203
+
204
+ success, message = uninstall_app(app_name, skip_confirmation)
205
+
206
+ print("\n" + "=" * 50)
207
+ if success:
208
+ print(f"✅ {message}")
209
+ sys.exit(0)
210
+ else:
211
+ print(f"❌ {message}")
212
+ sys.exit(1)
213
+
214
+ if __name__ == "__main__":
215
+ main()