-
Notifications
You must be signed in to change notification settings - Fork 0
/
package_manager.py
142 lines (104 loc) · 3.54 KB
/
package_manager.py
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
from abc import abstractmethod, ABC
import platform
import subprocess
from typing import Optional
def _command_exists(cmd: str) -> bool:
ret = subprocess.run(["command", "-v", cmd], stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, shell=True)
return ret.returncode == 0
class PackageManager(ABC):
name = "Base"
cached: Optional[type['PackageManager']] = None
@staticmethod
@abstractmethod
def detect() -> bool:
pass
@staticmethod
@abstractmethod
def is_dep_installed(dep: str) -> bool:
pass
@staticmethod
@abstractmethod
def install_dep(dep: str) -> None:
pass
class Apt(PackageManager):
name = "apt"
did_update = False
@staticmethod
def detect() -> bool:
return _command_exists(Apt.name)
@staticmethod
def is_dep_installed(dep: str) -> bool:
args = ["apt", "--installed", "list", dep, "-qq"]
out = subprocess.check_output(args, stderr=subprocess.DEVNULL,
text=True)
# could be [installed] or [installed,...], maybe something else too?
return "[installed" in out
@staticmethod
def install_dep(dep: str) -> None:
if not Apt.did_update:
subprocess.run(["sudo", "apt-get", "update"])
Apt.did_update = True
subprocess.run(["sudo", "apt-get", "install", "-y", dep], check=True)
class Pacman(PackageManager):
name = "pacman"
@staticmethod
def detect() -> bool:
return _command_exists(Pacman.name)
@staticmethod
def is_dep_installed(dep: str) -> bool:
ret = subprocess.run(["pacman", "-Qs", dep],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return ret.returncode == 0
@staticmethod
def install_dep(dep: str) -> None:
subprocess.run(["sudo", "pacman", "-Sy", dep, "--noconfirm"],
check=True)
class Brew(PackageManager):
name = "brew"
@staticmethod
def detect() -> bool:
return _command_exists(Brew.name)
@staticmethod
def is_dep_installed(dep: str) -> bool:
ret = subprocess.run(["brew", "list", dep],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL)
return ret.returncode == 0
@staticmethod
def install_dep(dep: str) -> None:
subprocess.run(["brew", "install", dep], check=True)
@staticmethod
def prefix(dep: str) -> str:
out = subprocess.check_output(["brew", "--prefix", dep], text=True)
return out.strip()
PACKAGE_MANAGERS = (
Apt,
Pacman,
Brew,
)
def get_package_manager() -> type[PackageManager]:
if PackageManager.cached is not None:
return PackageManager.cached
system = platform.system()
if system == "Darwin":
PackageManager.cached = Brew
return PackageManager.cached
for pm in PACKAGE_MANAGERS:
if pm.detect():
print(f"Detected package manager {pm.name}")
PackageManager.cached = pm
break
if PackageManager.cached is None:
raise RuntimeError("Couldn't detect a supported package manager")
return PackageManager.cached
def install_dependencies(deps: dict) -> None:
pm = get_package_manager()
pm_deps = deps[pm.name]
for dep in pm_deps:
if pm.is_dep_installed(dep):
print(f"{dep} is already installed")
continue
print(f"Installing {dep}...")
pm.install_dep(dep)