-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathinstaller.py
More file actions
323 lines (283 loc) · 13.1 KB
/
installer.py
File metadata and controls
323 lines (283 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
import os
import platform
import subprocess
import sys
import shutil
try:
from colorama import init as colorama_init
from colorama import Fore, Style
colorama_init()
_COLORAMA_AVAILABLE = True
except ImportError:
_COLORAMA_AVAILABLE = False
class _NoColor:
WHITE = CYAN = GREEN = BLUE = MAGENTA = LIGHTRED_EX = LIGHTBLUE_EX = RED = ''
class _NoStyle:
RESET_ALL = ''
Fore = _NoColor
Style = _NoStyle
SYSTEM_NAME = ""
def is_termux():
"""Detects if running in Termux environment on Android"""
# Check for Termux-specific environment variables and paths
return (os.environ.get('PREFIX', '').startswith('/data/data/com.termux') or
os.path.exists('/data/data/com.termux'))
def link(uri, label=None):
"""Formats a link using the given label
for console usage"""
if label is None:
label = uri
parameters = ''
# OSC 8 ; params ; URI ST <name> OSC 8 ;; ST
escape_mask = '\033]8;{};{}\033\\{}\033]8;;\033\\'
return escape_mask.format(parameters, uri, label)
def get_nvim_config_dir():
"""Returns the Neovim config directory path for a auto-detected operating system"""
home = os.path.expanduser("~")
if platform.system() == "Windows":
nvim_path = os.path.join(home, "AppData", "Local", "nvim")
print(f"{Fore.WHITE} - Determined config location should be at {nvim_path}")
return nvim_path
else:
nvim_path = os.path.join(home, ".config", "nvim")
print(f"{Fore.WHITE} - Determined config location should be at {nvim_path}")
return nvim_path
def create_symlink(source, dest):
"""Creates a symlink between the configuration location and
Neovims config directory for a given Operating system."""
try:
if not os.path.exists(source):
print(f"{Fore.LIGHTRED_EX} - Source does not exist, skipping: {source}")
return
parent = os.path.dirname(dest)
if parent and not os.path.exists(parent):
os.makedirs(parent, exist_ok=True)
if os.path.exists(dest):
if os.path.islink(dest) or os.path.isfile(dest):
os.remove(dest)
elif os.path.isdir(dest):
shutil.rmtree(dest)
os.symlink(source, dest)
except OSError as e:
print(f"Failed to create symlink {dest}: {e}")
def install_config():
"""Creates the symlink from whereever you run the installer from to the
Neocode config directory for the given system."""
print(f"{Fore.WHITE}---{Fore.CYAN} Symlinking NeoCode to config directory {Fore.WHITE}---")
config_dir = get_nvim_config_dir()
source_dir = os.path.join(os.path.dirname(__file__), 'nvim-config')
if not os.path.exists(config_dir):
print(f"{Fore.WHITE} - Nvim config directory doesn't exist...creating for you")
os.makedirs(config_dir)
elif os.path.exists(config_dir):
parent_dir = os.path.dirname(config_dir)
backup_base = os.path.join(parent_dir, 'nvim.backup')
backup_candidate = backup_base
idx = 1
while os.path.exists(backup_candidate):
backup_candidate = f"{backup_base}.{idx}"
idx += 1
print(f"{Fore.LIGHTRED_EX} - Existing Neovim config folder detected. Renaming folder to {backup_candidate}")
try:
shutil.move(config_dir, backup_candidate)
except OSError as e:
print(f"{Fore.RED}Failed to rename existing config directory: {e}")
return None
# Recreate the now-empty config directory so we can populate it
try:
os.makedirs(config_dir, exist_ok=True)
except OSError as e:
print(f"{Fore.RED}Failed to create new config directory {config_dir}: {e}")
return None
for item in os.listdir(source_dir):
s = os.path.join(source_dir, item)
d = os.path.join(config_dir, item)
try:
create_symlink(s, d)
except:
print(f"{Fore.RED}An error occured during the symlink! Please vist the Discord for support.")
return None
print(f"{Fore.LIGHTBLUE_EX} SYMLINK SUCCESSFUL! \n {Fore.WHITE}")
def determine_package_manager():
"""Returns the package manager name for the given system, to be used by
the install_dependencies function"""
print(f" {Fore.WHITE}--- {Fore.CYAN}Picking package manager based on OS {Fore.WHITE}---")
# Check for Termux first before checking freedesktop_os_release
if is_termux():
print(f" {Fore.WHITE}--- {Fore.CYAN}System is {Fore.WHITE}---Termux (Android)")
print(" - system is Termux! Use pkg. \n")
return "pkg"
system_info = platform.freedesktop_os_release()
system_name = system_info.get('ID')
print(f" {Fore.WHITE}--- {Fore.CYAN}System is {Fore.WHITE}---" + system_name)
SYSTEM_NAME = system_name
if system_name == 'fedora':
print(" - system is Fedora! Use DNF. \n")
return "dnf"
if system_name == 'ubuntu':
print(" - system is Ubuntu! Use apt. \n")
return "apt"
if system_name == 'darwin':
print(" - system is MacOS! Use brew. \n")
return "brew"
if system_name == 'endeavouros':
print(" - system is Endeavour! Use pacman. \n")
return "pacman"
if system_name == 'manjaro':
print(" - system is Manjaro! Use pacman. \n")
return "pacman"
if system_name == 'arch':
print(" - system is Arch! Use pacman. \n")
return "pacman"
def check_and_install_choco():
"""Installs Choco for Windows users without it."""
try:
subprocess.check_call(["choco", "--version"])
except subprocess.CalledProcessError:
print("- Chocolatey is not installed. Installing Chocolatey...")
subprocess.check_call(['powershell', '-Command', 'Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; Invoke-Expression ((New-Object System.Net.WebClient).DownloadString(\'https://chocolatey.org/install.ps1\'))'])
def check_and_install_brew():
"""Installs Brew for MacOS users who don't have it installed."""
try:
subprocess.check_call(["brew", "--version"])
except subprocess.CalledProcessError:
print("- Homebrew is not installed. Installing Homebrew...")
subprocess.check_call(['/bin/bash', '-c', "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"])
## TODO: Lazygit requires special commands to install for each OS. Will have to add those.
def install_dependencies():
"""Uses the package manager returned from determine_package_manager() to install
things the users needs to have in order to run Neocode."""
# Determine the package manger we should use
# based off the users OS
package_manager = determine_package_manager()
# Map package names -> representative executable or check name.
# If the executable exists on PATH we will skip installing the package.
dependencies = {
"ripgrep": "rg",
"fzf": "fzf",
"lazygit": "lazygit",
"fortune-mod": "fortune",
}
# Print header for this step
# and set the fore color for all console output for dep checks
print(f" {Fore.WHITE}--- {Fore.CYAN}Check and install dependencies {Fore.WHITE}---{Fore.MAGENTA}")
print
# Install pip if not already installed
try:
import pip
except ImportError:
subprocess.check_call([sys.executable, "-m", "ensurepip", "--upgrade"])
if platform.system() != "Linux":
# Install Python packages for Neovim
try:
import importlib.util
if importlib.util.find_spec('pynvim') is None:
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "pynvim"])
except Exception:
subprocess.check_call([sys.executable, "-m", "pip", "install", "--upgrade", "pynvim"])
# Ensure the package manager function returned SOMETHING
if package_manager:
# Install Neovim itself and only install missing dependencies
if is_termux():
# Termux (Android) - uses pkg without sudo
missing = []
for pkg, exe in dependencies.items():
if shutil.which(exe) is None:
missing.append(pkg)
# Install neovim if `nvim` is not available
if shutil.which('nvim') is None:
subprocess.check_call(["pkg", "install", "-y", "neovim"])
# Install missing dependencies
if missing:
for dep in missing:
subprocess.check_call(["pkg", "install", "-y", dep])
elif platform.system() == "Linux":
# Only install what's missing. Check executables first.
missing = []
for pkg, exe in dependencies.items():
if shutil.which(exe) is None:
missing.append(pkg)
# Install neovim if `nvim` is not available
if shutil.which('nvim') is None:
if package_manager == "apt":
subprocess.check_call(["sudo", package_manager, "install", "-y", "neovim"])
elif package_manager == "pacman":
subprocess.check_call(["sudo", package_manager, "-S", "neovim", "--noconfirm"])
elif package_manager == "dnf":
subprocess.check_call(["sudo", package_manager, "install", "-y", "neovim"])
if missing:
if package_manager == "apt":
for dep in missing:
subprocess.check_call(["sudo", package_manager, "install", "-y", dep])
elif package_manager == "pacman":
for dep in missing:
subprocess.check_call(["sudo", package_manager, "-S", dep, "--noconfirm"])
elif package_manager == "dnf":
for dep in missing:
subprocess.check_call(["sudo", package_manager, "install", "-y", dep])
elif platform.system() == "Darwin":
check_and_install_brew()
missing = []
for pkg, exe in dependencies.items():
if shutil.which(exe) is None:
missing.append(pkg)
if missing:
for dep in missing:
subprocess.check_call(["sudo", package_manager, "install", "-y", dep])
if shutil.which('nvim') is None:
subprocess.check_call(["sudo", package_manager, "install", "-y", "neovim"])
elif platform.system() == "Windows":
check_and_install_choco()
missing = []
for pkg, exe in dependencies.items():
if shutil.which(exe) is None:
missing.append(pkg)
if missing:
for dep in missing:
subprocess.check_call([package_manager, "install", dep])
if shutil.which('nvim') is None:
subprocess.check_call(["choco", "install", "neovim"])
else:
print("idk what even is your OS.")
# Handle the package manager function returning nothing/getting confused.
else:
print("Sorry...I couldn't determine your operating system to install NeoCode.")
def run_packer_install():
"""headlessly installs packer plugins for Neovim so first boot will be smooth"""
print(f" {Fore.WHITE}--- {Fore.CYAN}Run Packer install for clean first-startup {Fore.WHITE}---")
print(f" {Fore.MAGENTA}This may take a few minutes. Installing plugins...")
command = ["nvim", "--headless", "-c", "autocmd User PackerComplete quitall", "-c", "PackerSync"]
try:
# Use Popen to stream output in real-time
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
bufsize=1
)
# Print output line by line as it comes
for line in iter(process.stdout.readline, ''):
if line:
print(f"{Fore.MAGENTA} {line.rstrip()}")
process.wait()
if process.returncode == 0:
print(f"{Fore.GREEN} ✓ Packer sync completed successfully!")
else:
print(f"{Fore.RED} ✗ Packer sync exited with code {process.returncode}")
except FileNotFoundError:
print(f"{Fore.RED}Neovim not found in PATH. Please install Neovim and re-run this step.")
except Exception as e:
print(f"{Fore.RED}Packer sync failed: {e}")
print(f"{Fore.WHITE}Try opening Neovim and run `:PackerSync` manually, or install packer.nvim first.")
if __name__ == "__main__":
# try:
print(f"{Fore.WHITE}--- {Fore.GREEN} NeoCode Installer {Fore.WHITE}---")
print(f"{Fore.BLUE} Thanks for trying NeoCode!")
# print(f"{Fore.WHITE} Visit the {Fore.MAGENTA}{link("https://discord.gg/9tZq3WrU4p", "Discord")}{Fore.WHITE} for support. \n")
install_config()
install_dependencies()
run_packer_install()
print(f"{Fore.LIGHTBLUE_EX}Neovim configuration installed successfully!")
# except:
# print(f"{Fore.RED}An error occured during the install process! Sorry d00d!")