devcore-cli 1.0.1__tar.gz

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.
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: devcore-cli
3
+ Version: 1.0.1
4
+ Summary: DevCore โ€” WordPress & Laravel project automation CLI
5
+ Author: Puji Ermanto | <Engineer>
6
+ Author-email: puji@gmail.com
7
+ Requires-Python: >=3.8
8
+ Requires-Dist: jinja2>=3.1.2
9
+ Dynamic: author
10
+ Dynamic: author-email
11
+ Dynamic: requires-dist
12
+ Dynamic: requires-python
13
+ Dynamic: summary
@@ -0,0 +1,116 @@
1
+ # ๐Ÿง  DevCore CLI
2
+
3
+ DevCore adalah sistem otomatisasi untuk setup, konfigurasi, dan deployment stack WordPress modern melalui Command Line Interface (CLI).
4
+ Didesain untuk developer yang ingin mempercepat workflow WordPress Development, mulai dari inisialisasi proyek, konfigurasi environment, hingga sinkronisasi GitHub โ€” semua hanya lewat terminal.
5
+
6
+ ---
7
+
8
+ ## ๐Ÿš€ Fitur Utama
9
+
10
+ - โš™๏ธ **WordPress Auto Installer & Setup**
11
+ Buat project WordPress siap-pakai hanya dengan satu perintah.
12
+
13
+ - ๐Ÿงฉ **Template Builder**
14
+ Dukung stack: `WordPress + WooCommerce + Elementor`.
15
+
16
+ - ๐Ÿ’พ **Database Handler (SQLite)**
17
+ Menyimpan metadata project dan konfigurasi CLI dengan aman.
18
+
19
+ - ๐Ÿ” **Konfigurasi Environment Otomatis**
20
+ Termasuk rebuild & reset config.
21
+
22
+ - ๐Ÿ”„ **Auto Sync ke GitHub**
23
+ Login GitHub langsung dari CLI.
24
+
25
+ - ๐Ÿงฑ **Modular CLI Structure**
26
+ Setiap perintah terpisah secara modular di direktori `core/`.
27
+
28
+ ---
29
+
30
+ ## โšก Perintah Dasar DevCore
31
+
32
+ #### 1๏ธโƒฃ Login GitHub terlebih dahulu
33
+ ```bash
34
+ devcore login github
35
+ ```
36
+
37
+ #### Setup project
38
+ ```bash
39
+ devcore wp setup --generate
40
+ ```
41
+
42
+ 2๏ธโƒฃ Membuat template awal
43
+
44
+ ```bash
45
+ devcore new project --type wordpress --client "New-Commerce-Project" --stack "wordpress+woo"
46
+ ```
47
+
48
+ 3๏ธโƒฃ Build WordPress Project
49
+ ```bash
50
+ devcore wp init New-Commerce-Project
51
+ ```
52
+
53
+ 4๏ธโƒฃ Reset atau Rebuild Konfigurasi
54
+ ```bash
55
+ devcore config rebuild
56
+ devcore config reset
57
+ ```
58
+
59
+ 5๏ธโƒฃ Menghapus Database Project
60
+ ```bash
61
+ del devcore_projects.db
62
+ ```
63
+
64
+ #### Setup WordPress Plugins & Themes
65
+ Installs plugins and themes automatically using WP-CLI based on configuration files.
66
+
67
+ ```bash
68
+ devcore wp init New-Commerce-Project --include-setup
69
+ ```
70
+
71
+ ๐Ÿ“ Struktur Direktori DevCore
72
+ ```bash
73
+ dev-core-system/
74
+ โ”‚
75
+ โ”œโ”€โ”€ core/
76
+ โ”‚ โ”œโ”€โ”€ command_config.py
77
+ โ”‚ โ”œโ”€โ”€ command_new_setup.py
78
+ โ”‚ โ”œโ”€โ”€ db.py
79
+ โ”‚ โ”œโ”€โ”€ env_manager.py
80
+ โ”‚ โ””โ”€โ”€ wp_init.py
81
+ โ”‚ โ””โ”€โ”€ wp_dropdb.py
82
+ โ”‚ โ””โ”€โ”€ github_integration.py
83
+ โ”‚ โ””โ”€โ”€ template_engine.py
84
+ โ”‚
85
+ โ”œโ”€โ”€ templates/
86
+ โ”‚ โ””โ”€โ”€ .env.example (autogenerate)
87
+ โ”‚ โ””โ”€โ”€ docker-compose.yml (autogenerate)
88
+ โ”‚ โ””โ”€โ”€ README.md (autogenerate)
89
+ โ”‚
90
+ โ”œโ”€โ”€ .devcore.json
91
+ โ”œโ”€โ”€ devcore_projects.db
92
+ โ”œโ”€โ”€ devcore
93
+ โ”œโ”€โ”€ devcore.cmd
94
+ โ”œโ”€โ”€ README.md
95
+ โ””โ”€โ”€ DOCUMENTATION.md
96
+ ```
97
+
98
+ ๐Ÿงญ Roadmap Singkat
99
+ | Fase | Deskripsi | Status |
100
+ | ------- | -------------------------------------- | -------------- |
101
+ | Phase 1 | Core CLI & Config Handler | โœ… Selesai |
102
+ | Phase 2 | WordPress Stack Builder | ๐Ÿšง In Progress |
103
+ | Phase 3 | Integration Layer (GitHub, VPS Deploy) | โณ Planned |
104
+ | Phase 4 | Automation & Backup | โณ Planned |
105
+ | Phase 5 | DevCore Cloud Dashboard | ๐Ÿงฉ Research |
106
+ <img width="1024" height="1536" alt="devcore-system-roadmap" src="https://github.com/user-attachments/assets/a4b64b39-068e-4ef2-8044-b2aadf9d4b39" />
107
+
108
+ ๐Ÿ“œ Lisensi
109
+
110
+ MIT License ยฉ 2025 โ€” [DevCore Project Team > Puji Ermanto<pujiermanto@gmail.com>]
111
+
112
+ ๐Ÿ’ฌ Kontribusi
113
+
114
+ Pull Request, Issue, dan Feedback selalu terbuka.
115
+ Silakan buat branch baru sebelum commit ke main.
116
+
File without changes
@@ -0,0 +1,11 @@
1
+ # core/command_config.py
2
+ import os
3
+ from core.env_manager import CONFIG_FILE
4
+
5
+ def cmd_reset_config():
6
+ """Menghapus file konfigurasi DevCore"""
7
+ if os.path.exists(CONFIG_FILE):
8
+ os.remove(CONFIG_FILE)
9
+ print(f"โš™๏ธ Konfigurasi environment berhasil direset: {CONFIG_FILE}")
10
+ else:
11
+ print("โš ๏ธ Tidak ada file konfigurasi yang ditemukan untuk dihapus.")
@@ -0,0 +1,117 @@
1
+ # core/command_wp_setup.py
2
+ import os
3
+ import json
4
+ import subprocess
5
+ from pathlib import Path
6
+
7
+ GLOBAL_CONFIG = Path(__file__).resolve().parent.parent / ".devcore.json"
8
+
9
+ def load_global_config():
10
+ if GLOBAL_CONFIG.exists():
11
+ with open(GLOBAL_CONFIG, "r") as f:
12
+ return json.load(f)
13
+ return {}
14
+
15
+ def load_project_config(project_dir):
16
+ project_config = Path(project_dir) / "devcore_project.json"
17
+ if project_config.exists():
18
+ with open(project_config, "r") as f:
19
+ return json.load(f)
20
+ return {}
21
+
22
+ def run_wp_cli(commands, cwd):
23
+ """Menjalankan WP-CLI command di direktori WordPress project"""
24
+ for cmd in commands:
25
+ print(f"๐Ÿš€ Menjalankan: wp {cmd}")
26
+ try:
27
+ subprocess.run(["wp"] + cmd.split(), cwd=cwd, check=True)
28
+ except subprocess.CalledProcessError as e:
29
+ print(f"โŒ Gagal menjalankan: wp {cmd} ({e})")
30
+
31
+ def cmd_wp_setup(project_dir="."):
32
+ """Menginstall plugin & theme sesuai konfigurasi"""
33
+ print("๐Ÿ” Membaca konfigurasi DevCore...")
34
+ global_config = load_global_config()
35
+ project_config = load_project_config(project_dir)
36
+
37
+ plugins = project_config.get("plugins") or global_config.get("default_plugins", [])
38
+ themes = project_config.get("themes") or global_config.get("default_themes", [])
39
+
40
+ if not plugins and not themes:
41
+ print("โš ๏ธ Tidak ada plugin atau theme yang terdaftar untuk diinstall.")
42
+ return
43
+
44
+ print(f"๐Ÿ“ฆ Plugin terdeteksi: {plugins}")
45
+ print(f"๐ŸŽจ Theme terdeteksi: {themes}")
46
+
47
+ # Jalankan instalasi plugin
48
+ plugin_cmds = [f"plugin install {p} --activate" for p in plugins]
49
+ theme_cmds = []
50
+ for t in themes:
51
+ if t == "blocksy-child":
52
+ # Lewati install, nanti dibuat manual
53
+ continue
54
+ theme_cmds.append(f"theme install {t} --activate")
55
+
56
+ run_wp_cli(plugin_cmds + theme_cmds, cwd=project_dir)
57
+
58
+ print("โœ… Instalasi plugin & theme selesai!")
59
+
60
+ # Auto-generate Blocksy Child theme jika diperlukan
61
+ blocksy_child_dir = Path(project_dir) / "wp-content" / "themes" / "blocksy-child"
62
+ if "blocksy-child" in themes and not blocksy_child_dir.exists():
63
+ print("๐Ÿงฑ Membuat Blocksy Child theme otomatis...")
64
+ os.makedirs(blocksy_child_dir, exist_ok=True)
65
+
66
+ style_css = """/*
67
+ Theme Name: Blocksy Child
68
+ Template: blocksy
69
+ Author: DevCore System
70
+ Description: Child theme untuk kustomisasi Blocksy.
71
+ Version: 1.0
72
+ */
73
+ @import url("../blocksy/style.css");
74
+ """
75
+ functions_php = """<?php
76
+ add_action('wp_enqueue_scripts', function() {
77
+ wp_enqueue_style('blocksy-child-style', get_stylesheet_uri());
78
+ }, 20);
79
+ """
80
+ with open(blocksy_child_dir / "style.css", "w", encoding="utf-8") as f:
81
+ f.write(style_css)
82
+
83
+ with open(blocksy_child_dir / "functions.php", "w", encoding="utf-8") as f:
84
+ f.write(functions_php)
85
+
86
+ print("โœ… Blocksy Child theme berhasil dibuat.")
87
+ subprocess.run(["wp", "theme", "activate", "blocksy-child"], cwd=project_dir)
88
+
89
+
90
+
91
+ def generate_project_config(project_dir="."):
92
+ """Generate ulang devcore_project.json berdasarkan input user atau default global"""
93
+ print("๐Ÿงฑ Membuat ulang devcore_project.json ...")
94
+
95
+ global_config = load_global_config()
96
+
97
+ default_plugins = global_config.get("default_plugins", ["woocommerce"])
98
+ default_themes = global_config.get("default_themes", ["blocksy", "blocksy-child"])
99
+
100
+ project_name = input("๐Ÿ“ Nama proyek: ") or "New-Project"
101
+ plugins_input = input(f"๐Ÿ”Œ Plugin (pisahkan koma, default: {', '.join(default_plugins)}): ").strip()
102
+ themes_input = input(f"๐ŸŽจ Theme (pisahkan koma, default: {', '.join(default_themes)}): ").strip()
103
+
104
+ plugins = [p.strip() for p in plugins_input.split(",")] if plugins_input else default_plugins
105
+ themes = [t.strip() for t in themes_input.split(",")] if themes_input else default_themes
106
+
107
+ config_data = {
108
+ "project_name": project_name,
109
+ "plugins": plugins,
110
+ "themes": themes
111
+ }
112
+
113
+ config_path = Path(project_dir) / "devcore_project.json"
114
+ with open(config_path, "w", encoding="utf-8") as f:
115
+ json.dump(config_data, f, indent=2)
116
+
117
+ print(f"โœ… File devcore_project.json berhasil dibuat di {config_path}")
@@ -0,0 +1,43 @@
1
+ # core/db.py
2
+ import os
3
+ import sqlite3
4
+ from datetime import datetime
5
+
6
+ DB_PATH = os.path.join(os.getcwd(), "devcore_projects.db")
7
+
8
+ def ensure_db():
9
+ conn = sqlite3.connect(DB_PATH)
10
+ cur = conn.cursor()
11
+ cur.execute("""
12
+ CREATE TABLE IF NOT EXISTS projects (
13
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
14
+ name TEXT,
15
+ client_name TEXT,
16
+ project_type TEXT,
17
+ stack TEXT,
18
+ path TEXT,
19
+ repo_url TEXT,
20
+ status TEXT,
21
+ created_at TEXT
22
+ )
23
+ """)
24
+ conn.commit()
25
+ conn.close()
26
+
27
+ def add_project(name, client_name, project_type, stack, path, repo_url=None, status="created"):
28
+ ensure_db()
29
+ conn = sqlite3.connect(DB_PATH)
30
+ cur = conn.cursor()
31
+ cur.execute("""
32
+ INSERT INTO projects (name, client_name, project_type, stack, path, repo_url, status, created_at)
33
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
34
+ """, (name, client_name, project_type, stack, path, repo_url, status, datetime.utcnow().isoformat()))
35
+ conn.commit()
36
+ conn.close()
37
+
38
+ def update_repo_url(name, repo_url):
39
+ conn = sqlite3.connect(DB_PATH)
40
+ cur = conn.cursor()
41
+ cur.execute("UPDATE projects SET repo_url = ?, status = 'pushed' WHERE name = ?", (repo_url, name))
42
+ conn.commit()
43
+ conn.close()
@@ -0,0 +1,188 @@
1
+ import os
2
+ import json
3
+ import shutil
4
+ import platform
5
+ import subprocess
6
+ from pathlib import Path
7
+
8
+ CONFIG_FILE = Path(__file__).resolve().parent.parent / ".devcore.json"
9
+
10
+ def get_default_paths():
11
+ """Tentukan path default berdasarkan sistem operasi"""
12
+ system = platform.system().lower()
13
+
14
+ if "windows" in system:
15
+ return {
16
+ "xampp": Path("C:/xampp/htdocs"),
17
+ "laragon": Path("C:/laragon/www"),
18
+ "laradock": Path("C:/laradock/projects")
19
+ }
20
+ elif "darwin" in system: # macOS
21
+ return {
22
+ "xampp": Path("/Applications/XAMPP/htdocs"),
23
+ "laragon": Path.home() / "Sites/laragon",
24
+ "laradock": Path.home() / "Sites/laradock"
25
+ }
26
+ else: # Linux
27
+ return {
28
+ "xampp": Path("/opt/lampp/htdocs"),
29
+ "laragon": Path.home() / "Projects/laragon",
30
+ "laradock": Path.home() / "Projects/laradock"
31
+ }
32
+
33
+ def load_env_config():
34
+ """Muat konfigurasi environment, jika belum ada buat otomatis"""
35
+ defaults = get_default_paths()
36
+
37
+ if CONFIG_FILE.exists():
38
+ with open(CONFIG_FILE, "r") as f:
39
+ config = json.load(f)
40
+ else:
41
+ config = {k: str(v) for k, v in defaults.items()}
42
+ save_env_config(config)
43
+
44
+ # Pastikan semua path ada
45
+ for key, path_str in config.items():
46
+ path = Path(path_str)
47
+ if not path.exists():
48
+ path.mkdir(parents=True, exist_ok=True)
49
+ print(f"๐Ÿ“ Path '{path}' dibuat otomatis untuk {key}")
50
+
51
+ return config
52
+
53
+ def save_env_config(config):
54
+ with open(CONFIG_FILE, "w") as f:
55
+ json.dump(config, f, indent=4)
56
+ print(f"โœ… Konfigurasi environment tersimpan di {CONFIG_FILE}")
57
+
58
+ def choose_environment():
59
+ """Pilih environment dan pastikan path-nya ada"""
60
+ config = load_env_config()
61
+
62
+ print("Pilih environment lokal:")
63
+ print(f"[1] XAMPP ({config['xampp']})")
64
+ print(f"[2] Laragon ({config['laragon']})")
65
+ print(f"[3] Laradock ({config['laradock']})")
66
+
67
+ choice = input("> ").strip()
68
+ if choice == "1":
69
+ env, base = "xampp", Path(config["xampp"])
70
+ elif choice == "2":
71
+ env, base = "laragon", Path(config["laragon"])
72
+ elif choice == "3":
73
+ env, base = "laradock", Path(config["laradock"])
74
+ else:
75
+ print("โŒ Pilihan tidak valid, default ke current directory.")
76
+ env, base = "unknown", Path.cwd()
77
+
78
+ base.mkdir(parents=True, exist_ok=True)
79
+ print(f"๐Ÿ“‚ Environment dipilih: {env} โ†’ {base}")
80
+ return env, base
81
+
82
+
83
+ def set_custom_env():
84
+ config = load_env_config()
85
+ print("๐Ÿ› ๏ธ Konfigurasi environment custom:")
86
+ for key in config.keys():
87
+ new_path = input(f"Masukkan path untuk {key} (Enter untuk skip): ").strip()
88
+ if new_path:
89
+ config[key] = new_path.replace("\\", "/")
90
+ save_env_config(config)
91
+
92
+ def rebuild_env_config():
93
+ """Hapus dan buat ulang file konfigurasi environment DevCore"""
94
+ if CONFIG_FILE.exists():
95
+ CONFIG_FILE.unlink()
96
+ print("๐Ÿ—‘๏ธ File konfigurasi lama dihapus.")
97
+
98
+ defaults = {k: str(v) for k, v in get_default_paths().items()}
99
+ save_env_config(defaults)
100
+ print("โœ… Konfigurasi default berhasil dibuat ulang.")
101
+
102
+
103
+ def get_mysql_path(env_name: str) -> Path | None:
104
+ """Kembalikan path MySQL sesuai environment"""
105
+ system = platform.system().lower()
106
+
107
+ if "windows" in system:
108
+ if env_name == "xampp":
109
+ return Path("C:/xampp/mysql/bin")
110
+ elif env_name == "laragon":
111
+ # deteksi otomatis versi MySQL (jika ada)
112
+ base = Path("C:/laragon/bin/mysql")
113
+ if base.exists():
114
+ versions = sorted(base.glob("mysql*/bin"), reverse=True)
115
+ if versions:
116
+ return versions[0]
117
+ return base / "mysql-8.0.30-winx64/bin" # fallback
118
+ elif env_name == "laradock":
119
+ return Path("C:/laradock/mysql/bin")
120
+ else:
121
+ # Mac/Linux
122
+ if env_name == "xampp":
123
+ return Path("/opt/lampp/bin")
124
+ elif env_name == "laradock":
125
+ return Path.home() / "Projects/laradock/mysql/bin"
126
+
127
+ return None
128
+
129
+ def add_to_system_path(path_to_add: Path):
130
+ """Tambahkan folder ke Environment PATH (permanent)"""
131
+ system = platform.system().lower()
132
+ path_str = str(path_to_add.resolve())
133
+
134
+ if not path_to_add.exists():
135
+ print(f"โš ๏ธ Path tidak ditemukan: {path_to_add}")
136
+ return False
137
+
138
+ if "windows" in system:
139
+ # Ambil PATH sekarang dengan ekspansi variabel
140
+ current_path = os.environ.get("PATH", "")
141
+ if path_str in current_path:
142
+ print(f"โœ”๏ธ PATH sudah mengandung: {path_str}")
143
+ return True
144
+
145
+ new_path = f"{current_path};{path_str}"
146
+
147
+ try:
148
+ subprocess.run(f'setx PATH "{new_path}"', shell=True, check=True)
149
+ print(f"โœ… PATH berhasil ditambahkan di Windows: {path_str}")
150
+ except subprocess.CalledProcessError:
151
+ print(f"โŒ Gagal menambahkan PATH ke Windows.")
152
+ elif "darwin" in system or "linux" in system:
153
+ shell_rc = Path.home() / (".zshrc" if Path.home().joinpath(".zshrc").exists() else ".bashrc")
154
+ with open(shell_rc, "a") as f:
155
+ f.write(f'\n# Added by DevCore setup\nexport PATH="$PATH:{path_str}"\n')
156
+ print(f"โœ… PATH ditambahkan ke {shell_rc}: {path_str}")
157
+ else:
158
+ print("โš ๏ธ Sistem operasi tidak dikenali, PATH tidak diubah.")
159
+ return False
160
+
161
+ return True
162
+
163
+
164
+ def detect_mysql_cli():
165
+ """Cari lokasi file mysql.exe / mysql CLI di environment umum"""
166
+ # 1๏ธโƒฃ Coba cari di PATH sistem
167
+ mysql_path = shutil.which("mysql")
168
+ if mysql_path:
169
+ return Path(mysql_path)
170
+
171
+ # 2๏ธโƒฃ Coba lokasi bawaan Laragon
172
+ laragon_mysql = Path("C:/laragon/bin/mysql")
173
+ if laragon_mysql.exists():
174
+ for version_dir in sorted(laragon_mysql.glob("mysql*/bin/mysql.exe"), reverse=True):
175
+ return version_dir
176
+
177
+ # 3๏ธโƒฃ Coba lokasi bawaan XAMPP
178
+ xampp_mysql = Path("C:/xampp/mysql/bin/mysql.exe")
179
+ if xampp_mysql.exists():
180
+ return xampp_mysql
181
+
182
+ # 4๏ธโƒฃ Coba lokasi global Linux / Mac
183
+ if platform.system() != "Windows":
184
+ for path in ["/usr/bin/mysql", "/usr/local/mysql/bin/mysql"]:
185
+ if Path(path).exists():
186
+ return Path(path)
187
+
188
+ return None
@@ -0,0 +1,111 @@
1
+ import os
2
+ import json
3
+ import subprocess
4
+ import requests
5
+ from pathlib import Path
6
+
7
+ CONFIG_PATH = Path.home() / ".devcore" / "config.json"
8
+
9
+
10
+ # ==============================
11
+ # Utility: Config Management
12
+ # ==============================
13
+ def load_config():
14
+ if not CONFIG_PATH.exists():
15
+ return {}
16
+ with open(CONFIG_PATH, "r") as f:
17
+ return json.load(f)
18
+
19
+
20
+ def save_config(data):
21
+ CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
22
+ with open(CONFIG_PATH, "w") as f:
23
+ json.dump(data, f, indent=4)
24
+
25
+
26
+ # ==============================
27
+ # GitHub Login / Token Setup
28
+ # ==============================
29
+ def github_login():
30
+ print("Masukkan GitHub Personal Access Token (PAT):")
31
+ token = input("> ").strip()
32
+ if not token.startswith("ghp_") and not token.startswith("github_"):
33
+ print("โš ๏ธ Token tidak valid. Pastikan menggunakan format GitHub PAT.")
34
+ return
35
+
36
+ config = load_config()
37
+ config["github_token"] = token
38
+ save_config(config)
39
+ print("โœ… Token GitHub disimpan di ~/.devcore/config.json")
40
+
41
+
42
+ # ==============================
43
+ # GitHub Repo Creation
44
+ # ==============================
45
+ def create_github_repo(repo_name, private=True, description=""):
46
+ config = load_config()
47
+ token = config.get("github_token")
48
+ if not token:
49
+ print("โŒ Belum login GitHub. Jalankan 'devcore login github' dulu.")
50
+ return None
51
+
52
+ api_url = "https://api.github.com/user/repos"
53
+ headers = {"Authorization": f"token {token}"}
54
+ payload = {
55
+ "name": repo_name,
56
+ "private": private,
57
+ "description": description or f"Repository for {repo_name} with devcore system by puji",
58
+ }
59
+
60
+ response = requests.post(api_url, headers=headers, json=payload)
61
+ if response.status_code == 201:
62
+ repo_data = response.json()
63
+ print(f"โœ… Repository berhasil dibuat: {repo_data['html_url']}")
64
+ return repo_data["clone_url"]
65
+ else:
66
+ print("โŒ Gagal membuat repository:", response.text)
67
+ return None
68
+
69
+
70
+ # ==============================
71
+ # Local Git Initialization
72
+ # ==============================
73
+ def init_local_git(project_path, repo_url):
74
+ try:
75
+ # Cek apakah sudah ada repo git
76
+ git_dir = Path(project_path) / ".git"
77
+ if not git_dir.exists():
78
+ subprocess.run(["git", "init"], cwd=project_path, check=True)
79
+ subprocess.run(["git", "add", "."], cwd=project_path, check=True)
80
+ subprocess.run(["git", "commit", "-m", "Initial commit"], cwd=project_path, check=True)
81
+ subprocess.run(["git", "branch", "-M", "main"], cwd=project_path, check=True)
82
+ else:
83
+ # Sudah ada repo, pastikan ada perubahan baru
84
+ subprocess.run(["git", "add", "."], cwd=project_path, check=True)
85
+ subprocess.run(["git", "commit", "-m", "Sync commit"], cwd=project_path)
86
+
87
+ # Set remote origin (replace kalau sudah ada)
88
+ subprocess.run(["git", "remote", "remove", "origin"], cwd=project_path, stderr=subprocess.DEVNULL)
89
+ subprocess.run(["git", "remote", "add", "origin", repo_url], cwd=project_path, check=True)
90
+
91
+ # Push ke GitHub
92
+ subprocess.run(["git", "push", "-u", "origin", "main"], cwd=project_path, check=True)
93
+ print("๐Ÿš€ Project berhasil di-push ke GitHub.")
94
+ except subprocess.CalledProcessError as e:
95
+ print(f"โŒ Gagal menjalankan git command: {e}")
96
+
97
+
98
+
99
+ # ==============================
100
+ # Public API for CLI
101
+ # ==============================
102
+ def github_init(project_name, project_path):
103
+ print(f"๐Ÿ”ง Membuat repository untuk project: {project_name}")
104
+ repo_url = create_github_repo(project_name)
105
+ if repo_url:
106
+ init_local_git(project_path, repo_url)
107
+
108
+
109
+ def github_connect(project_path, existing_repo_url):
110
+ print(f"๐Ÿ”— Menghubungkan project ke repo: {existing_repo_url}")
111
+ init_local_git(project_path, existing_repo_url)
@@ -0,0 +1,17 @@
1
+ # core/template_engine.py
2
+ from jinja2 import Environment, FileSystemLoader
3
+ from pathlib import Path
4
+
5
+ TEMPLATE_DIR = Path.cwd() / "templates"
6
+
7
+ def render_readme(context):
8
+ env = Environment(loader=FileSystemLoader(TEMPLATE_DIR))
9
+ template = env.get_template("README.md.j2")
10
+ return template.render(context)
11
+
12
+ def generate_readme(project_path, context):
13
+ output_path = Path(project_path) / "README.md"
14
+ readme_content = render_readme(context)
15
+ with open(output_path, "w", encoding="utf-8") as f:
16
+ f.write(readme_content)
17
+ print("๐Ÿงพ README.md otomatis dibuat.")
@@ -0,0 +1,75 @@
1
+ # core/wp_dropdb.py
2
+ from core.env_manager import choose_environment
3
+ import os
4
+ import sqlite3
5
+ import subprocess
6
+ import shutil
7
+ from pathlib import Path
8
+
9
+ DB_PATH = os.path.join(os.getcwd(), "devcore_projects.db")
10
+
11
+ def detect_env():
12
+ """Deteksi environment (Docker / XAMPP / Unknown)"""
13
+ if Path("docker-compose.yml").exists():
14
+ return "docker"
15
+ elif "xampp" in str(Path.cwd()).lower() or "mysql" in str(Path.cwd()).lower():
16
+ return "xampp"
17
+ else:
18
+ return "unknown"
19
+
20
+ def confirm(prompt):
21
+ """Konfirmasi interaktif sebelum eksekusi fatal"""
22
+ ans = input(f"{prompt} (y/n): ").strip().lower()
23
+ return ans == "y"
24
+
25
+ def drop_wp_database(project_name):
26
+ env = detect_env()
27
+ print(f"๐Ÿงฉ Detected environment: {env}")
28
+
29
+ db_name = f"{project_name}_db".replace("-", "_").lower()
30
+
31
+ # Konfirmasi dulu
32
+ print(f"โš ๏ธ Ini akan menghapus permanen database '{db_name}' dan folder project '{project_name}'")
33
+ if not confirm("Lanjutkan penghapusan?"):
34
+ print("โŽ Dibatalkan oleh pengguna.")
35
+ return
36
+
37
+ # Hapus database (Docker / XAMPP)
38
+ if env == "docker":
39
+ print(f"๐Ÿงจ Menghapus volume Docker untuk {db_name}...")
40
+ try:
41
+ subprocess.run(["docker-compose", "down", "-v"], check=True)
42
+ print("โœ… Docker volume berhasil dihapus.")
43
+ except subprocess.CalledProcessError:
44
+ print("โš ๏ธ Gagal menghapus volume Docker.")
45
+ elif env == "xampp":
46
+ print(f"๐Ÿงจ Menghapus database lokal '{db_name}'...")
47
+ try:
48
+ subprocess.run(["mysql", "-u", "root", "-e", f"DROP DATABASE IF EXISTS {db_name};"], check=True)
49
+ print(f"โœ… Database {db_name} berhasil dihapus dari MySQL.")
50
+ except FileNotFoundError:
51
+ print("โš ๏ธ MySQL CLI tidak ditemukan. Pastikan MySQL ada di PATH.")
52
+ else:
53
+ print("โŒ Tidak bisa mendeteksi environment database, dilewati.")
54
+
55
+ # Hapus folder project
56
+ project_path = Path.cwd() / project_name
57
+ if project_path.exists():
58
+ print(f"๐Ÿ—‘๏ธ Menghapus folder project: {project_path}")
59
+ shutil.rmtree(project_path, ignore_errors=True)
60
+ print("โœ… Folder project berhasil dihapus.")
61
+ else:
62
+ print("โš ๏ธ Folder project tidak ditemukan, dilewati.")
63
+
64
+ # Hapus record dari SQLite
65
+ if os.path.exists(DB_PATH):
66
+ conn = sqlite3.connect(DB_PATH)
67
+ cur = conn.cursor()
68
+ cur.execute("DELETE FROM projects WHERE name = ?", (project_name,))
69
+ conn.commit()
70
+ conn.close()
71
+ print("๐Ÿงพ Record project dihapus dari devcore_projects.db.")
72
+ else:
73
+ print("โš ๏ธ File devcore_projects.db tidak ditemukan, dilewati.")
74
+
75
+ print(f"๐Ÿ”ฅ Cleanup project '{project_name}' selesai total!\n")
@@ -0,0 +1,286 @@
1
+ from core.github_integration import github_init
2
+ from core.db import add_project
3
+ from core.template_engine import generate_readme
4
+ from core.env_manager import choose_environment, get_mysql_path, add_to_system_path
5
+ from datetime import datetime
6
+
7
+ import os
8
+ import subprocess
9
+ import zipfile
10
+ import requests
11
+ import shutil
12
+ from pathlib import Path
13
+
14
+ def init_wp_project(project_name, include_setup=False):
15
+ # === 1. Pilih environment (xampp / laragon / laradock) ===
16
+ env, base_dir = choose_environment()
17
+ mysql_path = get_mysql_path(env)
18
+
19
+ # === 2. Setup MySQL path ===
20
+ if mysql_path:
21
+ print(f"๐Ÿ” Mengecek PATH MySQL untuk {env}: {mysql_path}")
22
+ add_to_system_path(mysql_path)
23
+ mysql_executable = str(mysql_path / "mysql")
24
+ else:
25
+ print("โš ๏ธ Tidak menemukan lokasi MySQL untuk environment ini.")
26
+ mysql_executable = "mysql" # fallback
27
+
28
+ project_dir = base_dir / project_name
29
+ wp_zip_path = project_dir / "wordpress.zip"
30
+ wp_url = "https://wordpress.org/latest.zip"
31
+
32
+ print(f"๐Ÿš€ Membuat project WordPress di {env.upper()}: {project_dir}")
33
+ os.makedirs(project_dir, exist_ok=True)
34
+
35
+ # === 3. Download WordPress core ===
36
+ print("โฌ‡๏ธ Downloading WordPress core...")
37
+ r = requests.get(wp_url)
38
+ with open(wp_zip_path, "wb") as f:
39
+ f.write(r.content)
40
+
41
+ # === 4. Extract dan pindahkan isi ===
42
+ print("๐Ÿ“ฆ Ekstrak WordPress...")
43
+ with zipfile.ZipFile(wp_zip_path, "r") as zip_ref:
44
+ zip_ref.extractall(project_dir)
45
+
46
+ wp_src = project_dir / "wordpress"
47
+ if wp_src.exists():
48
+ for item in wp_src.iterdir():
49
+ shutil.move(str(item), str(project_dir / item.name))
50
+ shutil.rmtree(wp_src)
51
+
52
+ wp_zip_path.unlink(missing_ok=True)
53
+
54
+ # === 5. Struktur tambahan ===
55
+ print("๐Ÿงฉ Membuat struktur Dev Core tambahan...")
56
+ os.makedirs(project_dir / "src/themes", exist_ok=True)
57
+ os.makedirs(project_dir / "src/plugins", exist_ok=True)
58
+
59
+ # === 6. File .env ===
60
+ wp_home = f"http://{project_name}.test" if env == "laragon" else f"http://localhost/{project_name}"
61
+ wp_siteurl = f"{wp_home}/wp"
62
+
63
+ env_content = f"""# Environment WordPress
64
+ DB_NAME={project_name}_db
65
+ DB_USER=root
66
+ DB_PASSWORD=
67
+ DB_HOST=localhost
68
+ WP_HOME={wp_home}
69
+ WP_SITEURL={wp_siteurl}
70
+ """
71
+ (project_dir / ".env").write_text(env_content)
72
+
73
+ # === 7. docker-compose.yml ===
74
+ docker_content = f"""version: '3.8'
75
+ services:
76
+ db:
77
+ image: mysql:5.7
78
+ container_name: {project_name}_db
79
+ environment:
80
+ MYSQL_ROOT_PASSWORD: root
81
+ MYSQL_DATABASE: {project_name}_db
82
+ volumes:
83
+ - ./db_data:/var/lib/mysql
84
+ ports:
85
+ - "3306:3306"
86
+
87
+ wordpress:
88
+ image: wordpress:latest
89
+ container_name: {project_name}_wp
90
+ depends_on:
91
+ - db
92
+ environment:
93
+ WORDPRESS_DB_HOST: db:3306
94
+ WORDPRESS_DB_USER: root
95
+ WORDPRESS_DB_PASSWORD: root
96
+ WORDPRESS_DB_NAME: {project_name}_db
97
+ ports:
98
+ - "8080:80"
99
+ volumes:
100
+ - ./:/var/www/html
101
+ """
102
+ (project_dir / "docker-compose.yml").write_text(docker_content)
103
+
104
+ # === 8. Git init ===
105
+ print("๐Ÿ“ค Inisialisasi Git repository...")
106
+ subprocess.run(["git", "init"], cwd=project_dir, check=False)
107
+ subprocess.run(["git", "add", "."], cwd=project_dir, check=False)
108
+ subprocess.run(["git", "commit", "-m", "Initialize WordPress project"], cwd=project_dir, check=False)
109
+ subprocess.run(["git", "branch", "-M", "main"], cwd=project_dir, check=False)
110
+
111
+ # === 9. Push otomatis ke GitHub ===
112
+ print("๐ŸŒ Menghubungkan ke GitHub...")
113
+ github_init(project_name, project_dir)
114
+
115
+ # === 10. Simpan metadata project ===
116
+ add_project(
117
+ name=project_name,
118
+ client_name="default",
119
+ project_type="wordpress",
120
+ stack="wordpress+docker",
121
+ path=str(project_dir),
122
+ repo_url=f"https://github.com/codesyariah122/{project_name}",
123
+ status="pushed"
124
+ )
125
+
126
+ print("๐Ÿ“Š Metadata project tersimpan di devcore_projects.db")
127
+
128
+ # === 11. Generate README ===
129
+ context = {
130
+ "name": project_name,
131
+ "client_name": "default",
132
+ "project_type": "wordpress",
133
+ "stack": "wordpress+docker",
134
+ "created_at": datetime.utcnow().isoformat()
135
+ }
136
+ generate_readme(project_dir, context)
137
+
138
+ # === 12. Optional: Buat database lokal ===
139
+ if env in ["xampp", "laragon"]:
140
+ db_name = project_name.lower().replace("-", "_").replace(" ", "_") + "_db"
141
+ print(f"๐Ÿงฉ Membuat database lokal '{db_name}'...")
142
+
143
+ try:
144
+ subprocess.run([mysql_executable, "-u", "root", "-e", f"CREATE DATABASE IF NOT EXISTS {db_name};"], check=True)
145
+ print(f"โœ… Database '{db_name}' berhasil dibuat di MySQL lokal.")
146
+ except FileNotFoundError:
147
+ print("โš ๏ธ MySQL CLI tidak ditemukan di PATH atau lokasi umum.")
148
+ except subprocess.CalledProcessError as e:
149
+ print(f"โŒ Gagal membuat database: {e}")
150
+
151
+ # === 12.1 Buat devcore_project.json default jika belum ada ===
152
+ config_path = project_dir / "devcore_project.json"
153
+ if not config_path.exists():
154
+ default_config = {
155
+ "project_name": project_name,
156
+ "plugins": ["woocommerce", "jetpack"],
157
+ "themes": ["blocksy", "blocksy-child"]
158
+ }
159
+ with open(config_path, "w", encoding="utf-8") as f:
160
+ import json
161
+ json.dump(default_config, f, indent=4)
162
+ print("๐Ÿงพ devcore_project.json default dibuat otomatis.")
163
+ else:
164
+ print("โœ… devcore_project.json sudah ada, skip pembuatan.")
165
+
166
+ # === 13. Install plugin & theme dari devcore_project.json ===
167
+ install_plugins_and_themes(project_dir)
168
+ print("๐ŸŽ‰ WordPress project berhasil dibuat lengkap!\n")
169
+
170
+ def install_plugins_and_themes(project_dir):
171
+ """Baca devcore_project.json lalu install plugin & theme sesuai daftar"""
172
+ import json
173
+ import shutil
174
+
175
+ config_path = project_dir / "devcore_project.json"
176
+ if not config_path.exists():
177
+ print("โš ๏ธ Tidak menemukan devcore_project.json, skip instalasi plugin/theme.")
178
+ return
179
+
180
+ with open(config_path, "r", encoding="utf-8") as f:
181
+ config = json.load(f)
182
+
183
+ plugins = config.get("plugins", [])
184
+ themes = config.get("themes", [])
185
+
186
+ if not plugins and not themes:
187
+ print("โ„น๏ธ Tidak ada plugin atau theme untuk diinstall.")
188
+ return
189
+
190
+ # === Cari wp-cli ===
191
+ wp_cli = shutil.which("wp")
192
+ if not wp_cli:
193
+ tools_dir = Path(__file__).resolve().parent.parent / "tools" / "wp-cli"
194
+ tools_dir.mkdir(parents=True, exist_ok=True)
195
+ wp_phar = tools_dir / "wp-cli.phar"
196
+
197
+ if not wp_phar.exists():
198
+ print("โฌ‡๏ธ WP-CLI belum ada. Mengunduh dari https://github.com/wp-cli/builds...")
199
+ url = "https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar"
200
+ r = requests.get(url, timeout=30)
201
+ wp_phar.write_bytes(r.content)
202
+ print(f"โœ… WP-CLI berhasil diunduh ke {wp_phar}")
203
+
204
+ php_path = shutil.which("php")
205
+ if not php_path:
206
+ print("โŒ PHP CLI tidak ditemukan di PATH. Instalasi plugin/theme dibatalkan.")
207
+ return
208
+
209
+ wp_cli_cmd = [php_path, str(wp_phar)]
210
+ else:
211
+ wp_cli_cmd = [wp_cli]
212
+
213
+ # === Clear cache sebelum instalasi ===
214
+ print("๐Ÿงน Membersihkan cache WP-CLI...")
215
+ subprocess.run(wp_cli_cmd + ["cache", "clear"], cwd=project_dir, check=False)
216
+
217
+ # === Pastikan wp-config.php sudah ada ===
218
+ wp_config = project_dir / "wp-config.php"
219
+ if not wp_config.exists():
220
+ print("๐Ÿงพ Membuat wp-config.php otomatis ...")
221
+ subprocess.run(
222
+ wp_cli_cmd + [
223
+ "config", "create",
224
+ "--dbname=" + f"{project_dir.name.lower().replace('-', '_')}_db",
225
+ "--dbuser=root",
226
+ "--dbpass=",
227
+ "--dbhost=localhost",
228
+ "--skip-check"
229
+ ],
230
+ cwd=project_dir,
231
+ check=False
232
+ )
233
+
234
+ # === Pastikan wp-config.php ada ===
235
+ db_name = f"{project_dir.name.lower().replace('-', '_')}_db"
236
+ wp_config = project_dir / "wp-config.php"
237
+
238
+ if not wp_config.exists():
239
+ print(f"๐Ÿงฑ Membuat wp-config.php untuk {db_name} ...")
240
+
241
+ subprocess.run([
242
+ wp_cli,
243
+ "config",
244
+ "create",
245
+ f"--dbname={db_name}",
246
+ "--dbuser=root",
247
+ "--dbpass=",
248
+ "--dbhost=localhost"
249
+ ], cwd=project_dir, check=False)
250
+
251
+ if wp_config.exists():
252
+ print("โœ… wp-config.php berhasil dibuat.")
253
+ else:
254
+ print("โš ๏ธ Gagal membuat wp-config.php, cek WP-CLI dan izin folder.")
255
+
256
+ # === Jalankan wp core install jika belum ada wp_options ===
257
+ print("โš™๏ธ Menjalankan instalasi WordPress awal ...")
258
+ subprocess.run(
259
+ wp_cli_cmd + [
260
+ "core", "install",
261
+ "--url=http://localhost/" + project_dir.name,
262
+ "--title=" + project_dir.name,
263
+ "--admin_user=admin",
264
+ "--admin_password=admin",
265
+ "--admin_email=admin@example.com"
266
+ ],
267
+ cwd=project_dir,
268
+ check=False
269
+ )
270
+
271
+ print("๐Ÿ”Œ Menginstal plugin dan theme sesuai devcore_project.json ...")
272
+
273
+ # Jalankan instalasi plugin
274
+ for plugin in plugins:
275
+ print(f"โžก๏ธ Install plugin: {plugin}")
276
+ subprocess.run(wp_cli_cmd + ["plugin", "install", plugin, "--activate"], cwd=project_dir, check=False)
277
+
278
+ # Jalankan instalasi theme
279
+ for theme in themes:
280
+ print(f"๐ŸŽจ Install theme: {theme}")
281
+ subprocess.run(wp_cli_cmd + ["theme", "install", theme, "--activate"], cwd=project_dir, check=False)
282
+
283
+ print("โœ… Semua plugin dan theme selesai diinstall.")
284
+
285
+
286
+
@@ -0,0 +1,13 @@
1
+ Metadata-Version: 2.4
2
+ Name: devcore-cli
3
+ Version: 1.0.1
4
+ Summary: DevCore โ€” WordPress & Laravel project automation CLI
5
+ Author: Puji Ermanto | <Engineer>
6
+ Author-email: puji@gmail.com
7
+ Requires-Python: >=3.8
8
+ Requires-Dist: jinja2>=3.1.2
9
+ Dynamic: author
10
+ Dynamic: author-email
11
+ Dynamic: requires-dist
12
+ Dynamic: requires-python
13
+ Dynamic: summary
@@ -0,0 +1,18 @@
1
+ README.md
2
+ pyproject.toml
3
+ setup.py
4
+ core/__init__.py
5
+ core/command_config.py
6
+ core/command_wp_setup.py
7
+ core/db.py
8
+ core/env_manager.py
9
+ core/github_integration.py
10
+ core/template_engine.py
11
+ core/wp_dropdb.py
12
+ core/wp_init.py
13
+ devcore_cli.egg-info/PKG-INFO
14
+ devcore_cli.egg-info/SOURCES.txt
15
+ devcore_cli.egg-info/dependency_links.txt
16
+ devcore_cli.egg-info/entry_points.txt
17
+ devcore_cli.egg-info/requires.txt
18
+ devcore_cli.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ devcore = devcore:main
@@ -0,0 +1 @@
1
+ jinja2>=3.1.2
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,20 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name="devcore-cli",
5
+ version="1.0.1",
6
+ author="Puji Ermanto | <Engineer>",
7
+ author_email="puji@gmail.com",
8
+ description="DevCore โ€” WordPress & Laravel project automation CLI",
9
+ packages=find_packages(),
10
+ include_package_data=True,
11
+ install_requires=[
12
+ "jinja2>=3.1.2",
13
+ ],
14
+ entry_points={
15
+ "console_scripts": [
16
+ "devcore=devcore:main",
17
+ ],
18
+ },
19
+ python_requires=">=3.8",
20
+ )