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.
@@ -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.1
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ş
@@ -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.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.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.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.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()