-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinstall
executable file
·530 lines (439 loc) · 22.1 KB
/
install
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
#!/usr/bin/env python3
#
# Set up a home directory on a Unix system.
import sys
import os
import pathlib
import subprocess
import shutil
import filecmp
def emulate_run(args, env=None):
p = subprocess.Popen(args, env=env)
if p.wait() == 0:
return True
return False
def emulate_backtick(args):
try:
output = subprocess.check_output(args, universal_newlines=True).rstrip('\n')
except Exception:
output = ''
return output
def guess_name_and_email():
name = ''
email = ''
# Check some environment variables
if name == '' and 'NAME' in os.environ:
name = os.environ['NAME']
if email == '' and 'EMAIL' in os.environ:
email = os.environ['EMAIL']
return name, email
def request_name_and_email(iname, iemail):
name = iname
email = iemail
while True:
yn = input('Use "%s" as the user\'s full name? (y/n): ' % (name))
if yn == 'y':
break
name = input('Enter the user\'s full name: ')
while True:
yn = input('Use "%s" as the user\'s email address? (y/n): ' % (email))
if yn == 'y':
break
email = input('Enter the user\'s email address: ')
return name, email
dotfiledirs = 'dotfiles'
# requires running form the root of the dotfiles directory
rootdir = os.path.abspath(os.path.dirname('install.py'))
if rootdir != os.getcwd():
print('error: must be run from the root of the dotfiles directory.')
sys.exit(1)
# requires HOME (set it temporarily if not set)
if 'HOME' in os.environ:
HOME = os.environ['HOME']
elif 'USERPROFILE' in os.environ:
HOME = os.environ['USERPROFILE']
os.environ['HOME'] = os.environ['USERPROFILE']
else:
print('error: HOME is not set, aborting.')
sys.exit(1)
# default the prefix to ~/opt/local
prefix = os.path.join(HOME, 'opt', 'local')
machine = None
if sys.platform.startswith('linux'):
print('Installing for Linux.')
machine = 'linux'
elif sys.platform.startswith('darwin'):
print('Installing for macOS.')
machine = 'macos'
elif sys.platform.startswith('cygwin'):
print('Installing for Cygwin.')
machine = 'cygwin'
elif sys.platform.startswith('win32'):
print('Installing for Windows.')
machine = 'windows'
else:
print('error: unknown machine "%s", aborting.' % (machine))
sys.exit(1)
# find executables
stow = shutil.which('stow')
git = shutil.which('git')
hg = shutil.which('hg')
emacs = shutil.which('emacs')
if __name__ == '__main__':
# create ~/.ssh/config.d
os.makedirs(os.path.join(HOME, '.ssh', 'config.d'), exist_ok=True)
# create ~/opt/local if desired
if not os.path.isdir(prefix):
yn = input('Create %s? (y/n): ' % prefix)
if yn == 'y':
os.makedirs(os.path.join(prefix, 'bin'))
os.makedirs(os.path.join(prefix, 'include'))
os.makedirs(os.path.join(prefix, 'lib'))
os.makedirs(os.path.join(prefix, 'man'))
os.makedirs(os.path.join(prefix, 'mnt'))
os.makedirs(os.path.join(prefix, 'share'))
os.makedirs(os.path.join(prefix, 'src'))
# requires stow
if not stow:
print('warning: "stow" is not available.')
yn = input('Continue without using stow (only recommended on Windows)? (y/n) ')
if yn != 'y':
sys.exit(1)
if stow:
# attempt to stow the dotfiles directory, clean up if fails.
if not emulate_run([stow, '--target', HOME, dotfiledirs]):
# allow user to clean up existing files
for root, directories, files in os.walk(dotfiledirs):
for n in files:
f = os.path.join(root, n)
home_f = os.path.join(HOME, pathlib.PurePath(f).relative_to(dotfiledirs))
if os.path.exists(home_f):
if f != home_f:
if filecmp.cmp(f, home_f, False):
yn = input('%s is identical to %s. Overwrite? (y/n): ' % (f, home_f))
if yn == 'y':
os.remove(home_f)
else:
yn = input('%s is different than %s. Overwrite anyway? (y/n): ' % (f, home_f))
if yn == 'y':
os.remove(home_f)
if not emulate_run([stow, '--target', HOME, dotfiledirs]):
print('error: some targets could not be linked to home directory.')
sys.exit(1)
else:
identical = True
for root, directories, files in os.walk(dotfiledirs):
for n in files:
f = os.path.join(root, n)
home_f = os.path.join(HOME, pathlib.PurePath(f).relative_to(dotfiledirs))
if f == os.path.join(dotfiledirs, '.stow-local-ignore'):
continue
if os.path.exists(home_f):
if f != home_f:
output = emulate_backtick(['fc', f, home_f])
lines = output.splitlines()
if len(lines) > 0 and lines[1] != 'FC: no differences encountered':
yn = input('%s is different than %s. Overwrite anyway? (y/n): ' % (f, home_f))
if yn == 'y':
os.remove(home_f)
else:
identical = False
if not identical:
print('error: not all targets copied to the home directory.')
sys.exit(1)
for root, directories, files in os.walk(dotfiledirs):
for n in files:
f = os.path.join(root, n)
home_f = os.path.join(HOME, pathlib.PurePath(f).relative_to(dotfiledirs))
if f == os.path.join(dotfiledirs, '.stow-local-ignore'):
continue
if not os.path.islink(os.path.abspath(home_f)):
if os.path.exists(home_f):
os.remove(home_f)
os.symlink(os.path.abspath(f), os.path.abspath(home_f))
# ask user for name and email address
name, email = guess_name_and_email()
print('Defaulting user\'s full name and email address to "%s <%s>".' % (name, email))
yn = input('Do you want to configure a name and email address? (y/n): ')
if yn == 'y':
name, email = request_name_and_email(name, email)
# ask to install any services
yn = input('Install services (launchd or systemd)? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_services')]):
print('warning: failed to install services.')
# set up machine-specific ~/.profile
profile_local = os.path.join(HOME, '.profile.local')
if not os.path.exists(profile_local):
print('Note that ~/.profile.local is used for machine-specific Bourne Shell (sh) settings.')
open(profile_local, 'a').close()
# set up machine-specific ~/.bashrc
bashrc_local = os.path.join(HOME, '.bashrc.local')
if not os.path.exists(bashrc_local):
print('Note that ~/.bashrc.local is used for machine-specific Bourne-again Shell (bash) settings.')
open(bashrc_local, 'a').close()
# set up machine-specific ~/.zshrc
zshrc_local = os.path.join(HOME, '.zshrc.local')
if not os.path.exists(zshrc_local):
print('Note that ~/.zshrc.local is used for machine-specific Z shell (zsh) settings.')
open(zshrc_local, 'a').close()
# create a link to run cmd with inputs
cmd = shutil.which('cmd.exe')
if cmd:
cmd_shortcut = os.path.join(HOME, 'Desktop', 'homedir cmd.lnk')
cmd_shortcut_name, cmd_shortcut_ext = os.path.splitext(os.path.basename(cmd_shortcut))
if not os.path.exists(cmd_shortcut):
print('Create a shortcut at "%s" containing the following command.' % (cmd_shortcut))
print('\tTarget: %s /c start %s\\.cmd.bat' % (cmd, HOME))
print('\tName: %s' % (os.path.basename(cmd_shortcut_name)))
# set up machine-specific ~/.cmdrc.bat
cmdrc_local = os.path.join(HOME, '.cmdrc.local.bat')
if not os.path.exists(cmdrc_local):
print('Note that ~/.cmdrc.local.bat is used for machine-specific cmd settings.')
open(cmdrc_local, 'a').close()
# configure git
if git:
# set up machine-specific ~/.gitconfig
gitconfig_machine = os.path.join(HOME, '.gitconfig.machine')
if not os.path.exists(gitconfig_machine):
print('Note that ~/.gitconfig.machine is used for machine-specific settings to .gitconfig.')
open(gitconfig_machine, 'a').close()
# set up a credential helper
if machine == 'linux':
emulate_run([git, 'config', '-f', gitconfig_machine, 'credential.helper', 'cache --timeout 3600'])
elif machine == 'macos':
emulate_run([git, 'config', '-f', gitconfig_machine, 'credential.helper', 'osxkeychain'])
elif machine == 'cygwin':
emulate_run([git, 'config', '-f', gitconfig_machine, 'credential.helper', 'cache --timeout 3600'])
elif machine == 'windows':
emulate_run([git, 'config', '-f', gitconfig_machine, 'credential.helper', 'wincred'])
else:
print('error: unknown machine "%s".' % (machine))
sys.exit(1)
# set up user-specific ~/.gitconfig
gitconfig_user = os.path.join(HOME, '.gitconfig.user')
if not os.path.exists(gitconfig_user):
print('Note that ~/.gitconfig.user is used for user-specific settings to .gitconfig.')
open(gitconfig_user, 'a').close()
# move user section and email for git to the user file
user_section_empty = True
proposed_name = emulate_backtick([git, 'config', '--global', 'user.name'])
if proposed_name != '':
user_section_empty = False
print('Moving user.name "%s" %s: ' % (proposed_name, gitconfig_user))
emulate_run([git, 'config', '-f', gitconfig_user, 'user.name', proposed_name])
emulate_run([git, 'config', '--global', '--unset', 'user.name'])
proposed_email = emulate_backtick([git, 'config', '--global', 'user.email'])
if proposed_email != '':
user_section_empty = False
print('Moving user.email "%s" %s: ' % (proposed_email, gitconfig_user))
emulate_run([git, 'config', '-f', gitconfig_user, 'user.email', proposed_email])
emulate_run([git, 'config', '--global', '--unset', 'user.email'])
proposed_signingKey = emulate_backtick([git, 'config', '--global', 'user.signingKey'])
if proposed_signingKey != '':
user_section_empty = False
print('Moving user.signingKey "%s" %s: ' % (proposed_signingKey, gitconfig_user))
emulate_run([git, 'config', '-f', gitconfig_user, 'user.signingKey', proposed_signingKey])
emulate_run([git, 'config', '--global', '--unset', 'user.signingKey'])
if not user_section_empty:
emulate_run([git, 'config', '--global', '--remove-section', 'user'])
git_name = emulate_backtick([git, 'config', '-f', gitconfig_user, 'user.name'])
git_email = emulate_backtick([git, 'config', '-f', gitconfig_user, 'user.email'])
# configure user name and email for git
if git_name != '' or git_email != '':
print('Using "%s <%s>" for the global git user name/email.' % (git_name, git_email))
else:
print('The global git user name/email is not set up.')
yn = input('Set up the global git user name/email? (y/n): ')
if yn == 'y':
# if not set, initialize with default user name and email
if git_name == '':
git_name = name
if git_email == '':
git_email = email
# request from the user
git_name, git_email = request_name_and_email(git_name, git_email)
# set user name/email in user-specific config
if git_name != '':
emulate_run([git, 'config', '-f', gitconfig_user, 'user.name', git_name])
else:
emulate_run([git, 'config', '-f', gitconfig_user, '--unset', 'user.name'])
if git_email != '':
emulate_run([git, 'config', '-f', gitconfig_user, 'user.email', git_email])
else:
emulate_run([git, 'config', '-f', gitconfig_user, '--unset', 'user.email'])
# configure mercurial
if hg:
# set up machine-specific ~/.hgrc
hgrc_machine = os.path.join(HOME, '.hgrc.machine')
if not os.path.exists(hgrc_machine):
print('Note that ~/.hgrc.machine is used for machine-specific settings to .hgrc.')
open(hgrc_machine, 'a').close()
# set up user-specific ~/.hgrc
hgrc_user = os.path.join(HOME, '.hgrc.user')
if not os.path.exists(hgrc_user):
print('Note that ~/.hgrc.user is used for user-specific settings to .hgrc.')
open(hgrc_user, 'a').close()
# @todo use hg to set up user
# configure emacs
if emacs:
# set up machine-specific ~/.emacs
emacs_machine = os.path.join(HOME, '.emacs.machine.el')
if not os.path.exists(emacs_machine):
print('Note that ~/.emacs.machine.el is used for machine-specific settings to .emacs.')
with open(emacs_machine, 'a') as f:
print(";;; package --- Machine-specific Emacs initialization. -*- lexical-binding: t; -*-", file=f)
print("", file=f)
print(";;; Commentary:", file=f)
print("", file=f)
print(";;; Code:", file=f)
print("", file=f)
print("(provide '.emacs.machine)", file=f)
print("", file=f)
print(";;; .emacs.machine.el ends here", file=f)
yn = input('Configure system-specific emacs? (y/n): ')
if yn == 'y':
# ensure that emacs knows a good host name
emacs_hostname = ''
# if not set, suggest the global system name for emacs
if emacs_hostname == '':
proposed_hostname = emulate_backtick([emacs, '--batch', '--no-init-file', '--eval', '(princ system-name)'])
if proposed_hostname != '':
yn = input('Use the current emacs system-name "%s" for emacs? (y/n): ' % (proposed_hostname))
if yn == 'y':
emacs_hostname = proposed_hostname
# if not set, suggest the user-specific system name for emacs
if emacs_hostname == '':
proposed_hostname = emulate_backtick([emacs, '--batch', '--no-init-file', '--load', emacs_machine, '--eval', '(princ system-name)'])
if proposed_hostname != '':
yn = input('Use the user-specific emacs system-name "%s" for emacs? (y/n): ' % (proposed_hostname))
if yn == 'y':
emacs_hostname = proposed_hostname
# if not set, suggest the hostname of the computer for emacs
if emacs_hostname == '':
proposed_hostname = os.uname()[1]
if proposed_hostname != '':
yn = input('Use the hostname "%s" for emacs? (y/n): ' % (proposed_hostname))
if yn == 'y':
emacs_hostname = proposed_hostname
# if not set, ask the user for the hostname
if emacs_hostname == '':
emacs_hostname = input('Enter system-name for emacs: ')
# set the system-name for emacs
# @todo nice to edit the lisp s-expressions directly
if emacs_hostname != '':
yn = input('Use the system-name "%s" for emacs? (y/n) ' % (emacs_hostname))
if yn == 'y':
print('Add the following to ~/.emacs.machine.el:')
print(' (setq system-name "%s")' % (emacs_hostname))
# set up user-specific ~/.emacs
emacs_user = os.path.join(HOME, '.emacs.user.el')
if not os.path.exists(emacs_user):
print('Note that ~/.emacs.user.el is used for user-specific settings to .emacs.')
with open(emacs_user, 'a') as f:
print(";;; package --- User-specific Emacs initialization. -*- lexical-binding: t; -*-", file=f)
print("", file=f)
print(";;; Commentary:", file=f)
print("", file=f)
print(";;; Code:", file=f)
print("", file=f)
print("(provide '.emacs.user)", file=f)
print("", file=f)
print(";;; .emacs.user.el ends here", file=f)
yn = input('Configure user-specific emacs? (y/n): ')
if yn == 'y':
emacs_name = ''
emacs_email = ''
# if not set, suggest the global user's full name for emacs
if emacs_name == '':
proposed_name = emulate_backtick([emacs, '--batch', '--no-init-file', '--eval', '(princ user-full-name)'])
if proposed_name != '':
yn = input('Use the current global user\'s full name "%s" for emacs? (y/n): ' % (proposed_name))
if yn == 'y':
emacs_name = proposed_name
# if not set, suggest the user-specific user's full name for emacs
if emacs_name == '':
proposed_name = emulate_backtick([emacs, '--batch', '--no-init-file', '--load', emacs_user, '--eval', '(princ user-full-name)'])
if proposed_name != '':
yn = input('Use the current user-specific user\'s full name "%s" for emacs? (y/n): ' % (proposed_name))
if yn == 'y':
emacs_name = proposed_name
# if not set, suggest the default user's full name for emacs
if emacs_name == '':
proposed_name = name
yn = input('Use the default user\'s full name "%s" for emacs? (y/n): ' % (proposed_name))
if yn == 'y':
emacs_name = proposed_name
# if not set, ask user for the user's full name for emacs
if emacs_name == '':
emacs_name = input('Enter the user\'s full name for emacs: ')
# if not set, suggest the global user's email for emacs
if emacs_email == '':
proposed_email = emulate_backtick([emacs, '--batch', '--no-init-file', '--eval', '(princ user-mail-address)'])
if proposed_email != '':
yn = input('Use the current global user\'s email "%s" for emacs? (y/n): ' % (proposed_email))
if yn == 'y':
emacs_email = proposed_email
# if not set, suggest the user-specific user's email for emacs
if emacs_email == '':
proposed_email = emulate_backtick([emacs, '--batch', '--no-init-file', '--load', emacs_user, '--eval', '(princ user-mail-address)'])
if proposed_email != '':
yn = input('Use the current user-specific user\'s email "%s" for emacs? (y/n): ' % (proposed_email))
if yn == 'y':
emacs_email = proposed_email
# if not set, suggest the default user's email for emacs
if emacs_email == '':
proposed_email = email
yn = input('Use the default user\'s email address "%s" for emacs? (y/n): ' % (proposed_email))
if yn == 'y':
emacs_email = proposed_email
# if not set, ask user for the user's email for emacs
if emacs_email == '':
emacs_email = input('Enter the user\'s email for emacs: ')
# set the user's full name and email for emacs
# @todo nice to edit the lisp s-expressions directly
if emacs_name != '':
yn = input('Use the user\'s full name "%s" for emacs? (y/n): ' % (emacs_name))
if yn == 'y':
print('Add the following to ~/.emacs.user.el:')
print(' (setq user-full-name "%s")' % (emacs_name))
if emacs_email != '':
yn = input('Use the user\'s email "%s" for emacs? (y/n): ' % (emacs_email))
if yn == 'y':
print('Add the following to ~/.emacs.user.el:')
print(' (setq user-mail-address "%s")' % (emacs_email))
# ask to install source packages
yn = input('Install source packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_source_packages')]):
print('warning: failed to install source packages.')
# ask to install python packages
yn = input('Install Python packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_python_packages')]):
print('warning: failed to install Python packages.')
# ask to install ruby packages
yn = input('Install Ruby packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_ruby_packages')]):
print('warning: failed to install Ruby packages.')
# ask to install node packages
yn = input('Install Node packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_node_packages')]):
print('warning: failed to install Node.js packages.')
# ask to install go packages
yn = input('Install Go packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_go_packages')]):
print('warning: failed to install Go packages.')
# ask to install rust packages
yn = input('Install Rust packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_rust_packages')]):
print('warning: failed to install Rust packages.')
# ask to install lua packages
yn = input('Install Lua packages? (y/n): ')
if yn == 'y':
if not emulate_run([sys.executable, os.path.abspath('install_lua_packages')]):
print('warning: failed to install Lua packages.')