-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathinstall.py
334 lines (250 loc) · 9.74 KB
/
install.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
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
324
325
326
327
328
329
330
331
332
333
334
#
# Copyright (C) 2020 IBM. All Rights Reserved.
#
# See LICENSE.txt file in the root directory
# of this source tree for licensing information.
#
# pylint: disable=no-name-in-module,import-error
import json
import os
import ssl
import sys
import argparse
import getpass
from distutils.dir_util import copy_tree
from distutils.dir_util import remove_tree
from distutils.file_util import copy_file
from clai.datasource.config_storage import ConfigStorage
from clai.datasource.stats_tracker import StatsTracker
from clai.server.agent_datasource import AgentDatasource
from clai.tools.anonymizer import Anonymizer
from clai.tools.colorize_console import Colorize
from clai.tools.console_helper import print_complete, print_error
from clai.tools.file_util import append_to_file, get_rc_file, is_windows, get_setup_file, get_rc_files
SUPPORTED_SHELLS = ['bash']
URL_BASH_PREEXEC = 'http://raw.githubusercontent.com/rcaloras/bash-preexec/master/bash-preexec.sh'
BASH_PREEXEC = 'bash-preexec.sh'
def valid_python_version():
return sys.version_info[0] == 3 and sys.version_info[1] >= 6
def is_root_user(args):
return os.geteuid() == 0 or args.demo_mode
def get_shell(args):
if args.shell is None:
return os.path.basename(os.getenv('SHELL', ''))
return args.shell
def clai_installed(path):
expand_path = os.path.expanduser(path)
return os.path.isfile(expand_path)
def binary_installed(path):
return os.path.exists(path)
def parse_args():
default_user_destdir = os.path.join(
os.path.expanduser('/opt/local/share'),
'clai',
)
parser = argparse.ArgumentParser(description='Install CLAI for all users.')
parser.add_argument(
'--shell',
help="if you like to install for different shell",
dest='shell',
action='store'
)
parser.add_argument(
'--demo',
help="if you like to jump installation restrictions",
dest='demo_mode',
action='store_true'
)
parser.add_argument(
'--system',
help="if you like install it for all users.",
dest='system',
action='store_true',
default=False
)
parser.add_argument(
'--unassisted',
help="Don't ask to he user for questions or inputs in the install process",
dest='unassisted',
action='store_true',
default=False
)
parser.add_argument(
'--no-skills',
help="Don't install the default skills",
dest='no_skills',
action='store_true',
default=False
)
parser.add_argument(
'-d', '--destdir', metavar='DIR', default=default_user_destdir,
help='set destination to DIR',
)
args = parser.parse_args()
if not valid_python_version():
print_error('You need install python 3.6 or upper is required.')
sys.exit(1)
if not is_root_user(args):
print_error('You need root privileges for complete the installation process.')
sys.exit(1)
if is_windows():
print_error("CLAI is not supported on Windows.")
sys.exit(1)
shell = get_shell(args)
if shell not in SUPPORTED_SHELLS:
print_error('%s is not supported yet.' % shell)
sys.exit(1)
if args.system:
if args.destdir != default_user_destdir:
print_error(
'Custom paths incompatible with --system option.')
sys.exit(1)
return args
def mkdir(path):
print("creating directory: %s" % path)
if not os.path.exists(path):
os.makedirs(path, exist_ok=True)
def cp_tree(from_path, to_path):
print("copying folder from %s to %s" % (from_path, to_path))
copy_tree(from_path, to_path)
def copy(file, to_path):
print("copying file from %s to %s" % (file, to_path))
copy_file(file, to_path, update=1)
def download_file(file_url, filename):
print("Download %s" % file_url)
from urllib import request
# pylint: disable=protected-access
ssl._create_default_https_context = ssl._create_unverified_context
request.urlretrieve(file_url, filename=filename)
def remove(path):
print("cleaning %s" % path)
remove_tree(path)
def install_plugins_dependencies(path, plugin):
print(f'installing dependencies of plugin {plugin}')
result = os.system(f'{path}/fileExist.sh {plugin}')
return result == 0
def cli_executable(cli_path):
os.system(f'chmod 777 {cli_path}/clai-run')
os.system(f'chmod 777 {cli_path}/fswatchlog')
os.system(f'chmod 777 {cli_path}/obtain-command-id')
os.system(f'chmod 777 {cli_path}/post-execution')
os.system(f'chmod 777 {cli_path}/process-command')
os.system(f'chmod 777 {cli_path}/restore-history')
def read_users(bin_path):
with open(bin_path + '/usersInstalled.json') as json_file:
users = json.load(json_file)
print(users)
return users
def register_the_user(bin_path, system):
users = read_users(bin_path)
user_path = os.path.expanduser(get_rc_file(system))
if user_path not in users:
users.append(user_path)
with open(bin_path + '/usersInstalled.json', 'w') as json_file:
json.dump(users, json_file)
def create_rc_file_if_not_exist(system):
rc_file_path = os.path.expanduser(get_rc_file(system))
if not os.path.isfile(rc_file_path):
open(rc_file_path, 'a').close()
def ask_to_user(text):
print(Colorize()
.info()
.append(f"{text} ")
.append("(y/n)")
.to_console())
while True:
command_input = input()
if command_input in ('y', 'yes'):
return True
if command_input in ('n', 'not'):
return False
print(Colorize()
.info()
.append('choose yes[y] or not[n]')
.to_console())
def save_report_info(unassisted, agent_datasource, bin_path, demo_mode):
enable_report = True
if demo_mode:
enable_report = False
elif not unassisted:
enable_report = \
ask_to_user(
"Would you like anonymously send debugging and usage information"
"to the CLAI team in order to help improve it? (y/n)")
agent_datasource.mark_report_enable(enable_report)
stats_tracker = StatsTracker(sync=True, anonymizer=Anonymizer(alternate_path=f'{bin_path}/anonymize.json'))
stats_tracker.report_enable = enable_report
stats_tracker.log_install(getpass.getuser())
def execute(args):
unassisted = args.unassisted
no_skills = args.no_skills
demo_mode = args.demo_mode
bin_path = os.path.join(args.destdir, 'bin')
code_path = os.path.join(bin_path, 'clai')
cli_path = os.path.join(bin_path, 'bin')
temp_path = '~/tmp'
mkdir(f"{temp_path}/")
create_rc_file_if_not_exist(args.system)
if clai_installed(get_setup_file()):
print_error('CLAI is already in you system. You should execute uninstall first')
sys.exit(1)
if not binary_installed(bin_path):
mkdir(bin_path)
mkdir(code_path)
cp_tree('./clai', code_path)
cp_tree('./bin', cli_path)
copy('./scripts/clai.sh', bin_path)
copy('./scripts/saveFilesChanges.sh', bin_path)
copy('./configPlugins.json', bin_path)
copy('./usersInstalled.json', bin_path)
copy('./anonymize.json', bin_path)
copy('./scripts/fileExist.sh', bin_path)
os.system(f'chmod 775 {bin_path}/saveFilesChanges.sh')
os.system(f'chmod 775 {bin_path}/fileExist.sh')
os.system(f'chmod -R 777 {code_path}/server/plugins')
os.system(f'chmod 777 {bin_path}/clai.sh')
os.system(f'chmod 666 {bin_path}/configPlugins.json')
os.system(f'chmod 666 {bin_path}/anonymize.json')
os.system(f'chmod -R 777 {bin_path}')
cli_executable(cli_path)
download_file(URL_BASH_PREEXEC, filename='%s/%s' % (temp_path, BASH_PREEXEC))
copy('%s/%s' % (temp_path, BASH_PREEXEC), bin_path)
register_the_user(bin_path, args.system)
append_setup_to_file(get_setup_file(), bin_path)
register_file(args.system)
if not no_skills:
agent_datasource = AgentDatasource(
config_storage=ConfigStorage(alternate_path=f'{bin_path}/configPlugins.json'))
plugins = agent_datasource.all_plugins()
for plugin in plugins:
if plugin.default:
installed = install_plugins_dependencies(bin_path, plugin.pkg_name)
if installed:
agent_datasource.mark_plugins_as_installed(plugin.name, None)
save_report_info(unassisted, agent_datasource, bin_path, demo_mode)
remove(f"{temp_path}/")
os.system(f'chmod -R 777 /var/tmp')
print_complete("CLAI has been installed correctly, you need restart your shell.")
def register_file(system):
rc_files = get_rc_files(system)
for file in rc_files:
print(f"registering {file}")
append_to_file(file, "# CLAI setup\n")
append_to_file(file, 'if ! [ ${#preexec_functions[@]} -eq 0 ]; then\n')
append_to_file(file, ' if ! [[ " ${preexec_functions[@]} " =~ " preexec_override_invoke " ]]; then\n')
append_to_file(file, f" source {get_setup_file()} \n")
append_to_file(file, ' fi\n')
append_to_file(file, 'else\n')
append_to_file(file, f" source {get_setup_file()} \n")
append_to_file(file, 'fi\n')
append_to_file(file, "# End CLAI setup\n")
def append_setup_to_file(rc_path, bin_path):
append_to_file(rc_path, "\n export CLAI_PATH=%s" % bin_path)
append_to_file(rc_path, "\n export PYTHONPATH=%s" % bin_path)
append_to_file(
rc_path,
"\n[[ -f %s/bash-preexec.sh ]] && source %s/bash-preexec.sh" % (bin_path, bin_path))
append_to_file(
rc_path,
"\n[[ -f %s/clai.sh ]] && source %s/clai.sh" % (bin_path, bin_path))
sys.exit(execute(parse_args()))