-
Notifications
You must be signed in to change notification settings - Fork 0
/
install_tarstall
executable file
·291 lines (250 loc) · 10.3 KB
/
install_tarstall
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
#!/usr/bin/python3
"""tarstall: A package manager for managing archives
Copyright (C) 2022 hammy275
tarstall is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
tarstall is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with tarstall. If not, see <https://www.gnu.org/licenses/>."""
import sys
import os
from subprocess import call, DEVNULL
from shutil import which, rmtree
try:
columns = os.get_terminal_size()[0]
except OSError:
columns = 80
def progress(val, msg=" "):
"""Update Progress of Operation.
Updates a progress bar (if we have a GUI) as tarstall processes run
Args:
val (int/float): Value to update the progress bar to.
msg (str): Message to display on right side of progress bar. Defaults to " ".
Stole this from tarstall (my own program) lol
"""
start_chars = "Progress ({}%): ".format(str(int(val)))
end_chars = msg
end_buffer = 80
full_squares = int(val * 0.01 * (columns - len(start_chars) - end_buffer))
empty_squares = columns - len(start_chars) - end_buffer - full_squares
end = "\r"
if val == 100:
end = "\n"
print(start_chars + "■"*full_squares + "□"*empty_squares + end_chars, end=end)
def status(msg):
"""Print Status Message.
Prints "major" messages for the end user.
Args:
msg (str): Message to print.
"""
print("")
print("#"*int(columns*0.75))
print(msg)
print("#"*int(columns*0.75))
def run(cmds, verbose):
"""Run Command.
A tiny function that simply uses 'call', and returns whether or not the call exited successfully.
Args:
cmds (str[]): The command list to pass to call()
verbose (bool): Whether to show output.
Returns:
bool: Whether or not the call() succeeded.
"""
out = DEVNULL
if verbose:
out = None
err_code = call(cmds, stdout=out, stderr=out)
return err_code == 0
def install_package(pkg, verbose):
"""Installs a Package.
Installs the specified package using the distro's package manager.
Currently supports the following package managers:
* apt
* apt-get
* dnf
* pacman
Dictionary format:
If a given package has a different name depending on the package manager (ie. Debian systems having 'python3-tk'
while Arch systems having 'tk'), a dict can be used to specify that.
Example:
{"apt": "python3-tk", "dnf": "python3-tkinter", "pacman": "tk"}
The above example will install the package "python3-tk" if the user is using apt OR apt-get,
"python3-tkinter" if the user is using dnf, or "tk" if the user is using pacman.
Args:
pkg (str/dict): If a string, the package to install. See above for dictionary format.
verbose (bool): Whether to print output
"""
if type(pkg) is not str:
if which("apt") is not None or which("apt-get") is not None:
pkg = pkg["apt"]
elif which("dnf") is not None:
pkg = pkg["dnf"]
elif which("pacman") is not None:
pkg = pkg["pacman"]
else:
status("This script only supports automatic program installtion through apt(-get), dnf, and pip; please install {} manually!".format(pkg))
sys.exit(1)
if which("apt") is not None:
passed = run(["sudo", "apt", "install", pkg, "-y"], verbose)
elif which("apt-get") is not None:
passed = run(["sudo", "apt-get", "install", pkg, "-y"], verbose)
elif which("dnf") is not None:
passed = run(["sudo", "dnf", "install", pkg, "-y"], verbose)
elif which("pacman") is not None:
passed = run(["sudo", "pacman", "-S", pkg, "--noconfirm"], verbose)
else:
status("This script only supports automatic program installtion through apt(-get), dnf, and pip; please install {} manually!".format(pkg))
sys.exit(1)
if not passed:
status("Error installing package {}! Please install it manually, and/or read the error above for information how to resolve this error!".format(pkg))
sys.exit(1)
def setup(skip_questions=False, verbose=False):
# Checks
status("Checking for anything missing for tarstall setup")
supported_package_managers = ["apt", "apt-get", "dnf", "pacman"]
supported_shells = ["bash", "zsh", "fish"]
if which("sudo") is None:
status("Please install 'sudo'!")
sys.exit(1)
supported = False
for manager in supported_package_managers:
if which(manager) is not None:
supported = True
break
if not supported:
status("You currently don't have a supported package manager! Currently supported package managers are: " + ", ".join(supported_package_managers))
sys.exit(1)
shell = os.environ["SHELL"]
shell_supported = False
for sshell in supported_shells:
if sshell in shell:
shell_supported = True
break
msg = "All of the checks passed!"
if not shell_supported:
msg = "WARNING: YOUR SHELL IS NOT SUPPORTED!\n\nYOU WILL HAVE TO MANUALLY ADD ~/.tarstall/tarstall_execs TO YOUR PATH, " \
"AND ANY PATH/BINLINK FUNCTIONS WILL NOT WORK!\nWOULD YOU LIKE TO PROCEED WITH INSTALLATION ANYWAYS?"
status(msg)
should_proceed = input("Type 'YES' to continue; anything else to exit... ")
if should_proceed.lower() != "yes":
status("Installation cancelled!")
sys.exit(1)
msg = "All of the checks besides the shell check passed, while the shell check was skipped by the user!"
print("{}\n\n\n".format(msg))
# User information
if not skip_questions:
status("Welcome!\nThis installer is going to install tarstall! You'll need to enter your sudo password at some points. " +
"This will use your distro's package manager along with pip to install the dependencies required for tarstall!")
cancel = input("When you're ready to start installation, press ENTER! If you would like to cancel, type 'c', then press ENTER!")
if cancel.lower() == 'c':
status("Cancelling tarstall setup...")
sys.exit()
# Install requirements obtained through package manager
if verbose:
status("Installing tarstall's package requirements")
else:
progress(0, "Installing package requirements")
if which("git") is None:
install_package("git", verbose)
else:
if verbose:
print("git is already installed!")
if not verbose:
progress(10, "Installing package requirements")
if which("wget") is None:
install_package("wget", verbose)
else:
if verbose:
print("wget is already installed!")
if not verbose:
progress(15, "Installing package requirements")
if which("rsync") is None:
install_package("rsync", verbose)
else:
if verbose:
print("rsync is already installed!")
if not verbose:
progress(20, "Installing package requirements")
try:
import tkinter
del tkinter
except ImportError:
install_package({"apt": "python3-tk", "dnf": "python3-tkinter", "pacman": "tk"}, verbose)
if not verbose:
progress(30, "Installing package requirements")
# Clone repository
if verbose:
status("Getting a copy of tarstall")
else:
progress(30, "Downloading tarstall")
try:
rmtree("/tmp/tarstall-setup")
except FileNotFoundError:
pass
os.mkdir("/tmp/tarstall-setup")
os.chdir("/tmp/tarstall-setup")
if not verbose:
progress(32, "Downloading tarstall")
if not run(["git", "clone", "https://github.com/hammy275/tarstall.git"], verbose):
status("Error while getting the tarstall repository!")
try:
rmtree("/tmp/tarstall-setup")
except FileNotFoundError:
pass
sys.exit(1)
os.chdir("/tmp/tarstall-setup/tarstall")
# Install Python requirements
if verbose:
status("Installing tarstall's Python requirements")
else:
progress(70, "Installing Python requirements")
if not run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt", "--user"], verbose):
install_package({"apt": "python3-pip", "dnf": "python3-pip", "pacman": "python-pip"}, verbose)
if not run([sys.executable, "-m", "pip", "install", "-r", "requirements.txt", "--user"], verbose) or not \
run([sys.executable, "-m", "pip", "install", "-r", "requirements-gui.txt", "--user"], verbose):
status("An error occured while installing the GUI requirements!")
sys.exit(1)
# Pass control to tarstall
if verbose:
status("Running tarstall installation")
else:
progress(100, "Continuing setup from tarstall\n")
if not run([sys.executable, "./tarstall_execs/tarstall", "-f"], True):
status("Error installing tarstall! Please view the information above!!!")
sys.exit(1)
# Removing tarstall setup directory
try:
rmtree("/tmp/tarstall-setup")
except FileNotFoundError:
pass
def help_out():
print("""
Usage: install_tarstall [optional: OPTIONS]
Options:
\t-h, --help\tDisplay this help message and exit
\t-s, --skip-questions\tSkip prompting before installing tarstall
\t-v, --verbose\tShow status of setup verbosely instead of using a progress bar
""")
def parse_args():
if "-h" in sys.argv or "--help" in sys.argv:
help_out()
else:
expected_argv_length = 1
skip_questions = "-s" in sys.argv or "--skip-questions" in sys.argv
if skip_questions:
expected_argv_length += 1
verbose = "-v" in sys.argv or "--verbose" in sys.argv
if verbose:
expected_argv_length += 1
# Bail if there's some bad parameter
if len(sys.argv) != expected_argv_length:
help_out()
else:
setup(skip_questions, verbose)
if __name__ == "__main__":
parse_args()