clapp-pm 1.0.0__py3-none-any.whl → 1.0.1__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.
- {clapp_pm-1.0.0.data → clapp_pm-1.0.1.data}/data/version.json +1 -1
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/METADATA +1 -1
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/RECORD +11 -7
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/top_level.txt +4 -0
- install_command.py +236 -0
- list_command.py +262 -0
- publish_command.py +253 -0
- uninstall_command.py +215 -0
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/WHEEL +0 -0
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/entry_points.txt +0 -0
- {clapp_pm-1.0.0.dist-info → clapp_pm-1.0.1.dist-info}/licenses/LICENSE +0 -0
@@ -5,14 +5,18 @@ cli_commands.py,sha256=ffJeRJTeipzHkSXhIUKrGzmymqtuv9bKCCQ2WGMQUD4,12482
|
|
5
5
|
dependency_resolver.py,sha256=slskJhZJ5rRIv0Gcefd-cakHxdYl45bPwE4s_0APpsA,8562
|
6
6
|
doctor_command.py,sha256=VpYx5isO-EI9EmCqlyAvGIJWasMaDDLmU7MDYBOz3kc,9446
|
7
7
|
info_command.py,sha256=b74Pl1-x_PSezALx-lS3FkEcVNTF7a9McTKk5XTDhjM,5629
|
8
|
+
install_command.py,sha256=tkEMuezZ_-GWCikMfzZzkL0OjnRcyNixtW9OC749GDw,7789
|
8
9
|
installer.py,sha256=4ojp3FCTw9-VxJhBzadO9oNBFWDHw5IQsMqXlwvKH_g,10039
|
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.
|
29
|
-
clapp_pm-1.0.
|
32
|
+
clapp_pm-1.0.1.data/data/version.json,sha256=S7yOmQU4A-UqpUE3AX0vmjehgSS--fxuVEPG1YcIvDo,238
|
33
|
+
clapp_pm-1.0.1.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.
|
37
|
-
clapp_pm-1.0.
|
38
|
-
clapp_pm-1.0.
|
39
|
-
clapp_pm-1.0.
|
40
|
-
clapp_pm-1.0.
|
40
|
+
clapp_pm-1.0.1.dist-info/METADATA,sha256=Tsv7OwP1PgQYq_SNfLiW-PIya0u1S6YbruJbvBhdZ84,3979
|
41
|
+
clapp_pm-1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
42
|
+
clapp_pm-1.0.1.dist-info/entry_points.txt,sha256=7j-3pQVpQfnaVzUV83g7zlCA30ePlnXkhHLAGGz9xrQ,36
|
43
|
+
clapp_pm-1.0.1.dist-info/top_level.txt,sha256=90PY3qqc7ZdaDsLvwHtQiUG0OZqpsjxw_zkjiTTFY0I,351
|
44
|
+
clapp_pm-1.0.1.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
|
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()
|
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()
|
File without changes
|
File without changes
|
File without changes
|