Merge branch 'namespaced' of git://tremily.us/swc-version-control-git into assembled
[swc-boot-camp.git] / setup / windows-installer / swc-windows-installer.py
1 #!/usr/bin/env python
2
3 """Software Carpentry Windows Installer
4
5 Helps mimic a *nix environment on Windows with as little work as possible.
6
7 The script:
8 * Installs nano and makes it accessible from msysgit
9 * Provides standard nosetests behavior for msysgit
10
11 To use:
12
13 1. Install Python, IPython, and Nose.  An easy way to do this is with
14    the Anaconda CE Python distribution
15    http://continuum.io/anacondace.html
16 2. Install msysgit
17    http://code.google.com/p/msysgit/downloads/list?q=full+installer+official+git
18 3. Run swc_windows_installer.py
19    You should be able to simply double click the file in Windows
20
21 """
22
23 import hashlib
24 try:  # Python 3
25     from io import BytesIO as _BytesIO
26 except ImportError:  # Python 2
27     from StringIO import StringIO as _BytesIO
28 import os
29 import re
30 try:  # Python 3
31     from urllib.request import urlopen as _urlopen
32 except ImportError:  # Python 2
33     from urllib2 import urlopen as _urlopen
34 import zipfile
35
36
37 def zip_install(url, sha1, install_directory):
38     """Download and install a zipped bundle of compiled software"""
39     r = _urlopen(url)
40     zip_bytes = r.read()
41     download_sha1 = hashlib.sha1(zip_bytes).hexdigest()
42     if download_sha1 != sha1:
43         raise ValueError(
44             'downloaded {!r} has the wrong SHA1 hash: {} != {}'.format(
45                 url, downloaded_sha1, sha1))
46     zip_io = _BytesIO(zip_bytes)
47     zip_file = zipfile.ZipFile(zip_io)
48     if not os.path.isdir(install_directory):
49         os.makedirs(install_directory)
50         zip_file.extractall(install_directory)
51
52
53 def install_nano(install_directory):
54     """Download and install the nano text editor"""
55     zip_install(
56         url='http://www.nano-editor.org/dist/v2.2/NT/nano-2.2.6.zip',
57         sha1='f5348208158157060de0a4df339401f36250fe5b',
58         install_directory=install_directory)
59
60
61 def create_nosetests_entry_point(python_scripts_directory):
62     """Creates a terminal-based nosetests entry point for msysgit"""
63     contents = '\n'.join([
64             '#!/usr/bin/env/ python',
65             'import sys',
66             'import nose',
67             "if __name__ == '__main__':",
68             '    sys.exit(nose.core.main())',
69             '',
70             ])
71     if not os.path.isdir(python_scripts_directory):
72         os.makedirs(python_scripts_directory)
73     with open(os.path.join(python_scripts_directory, 'nosetests'), 'w') as f:
74         f.write(contents)
75
76
77 def update_bash_profile(extra_paths=()):
78     """Create or append to a .bash_profile for Software Carpentry
79
80     Adds nano to the path, sets the default editor to nano, and adds
81     additional paths for other executables.
82     """
83     lines = [
84         '',
85         '# Add paths for Software-Carpentry-installed scripts and executables',
86         'export PATH=$PATH:{}'.format(':'.join(
87             make_posix_path(path) for path in extra_paths),),
88         '',
89         '# Make nano the default editor',
90         'export EDITOR=nano',
91         '',
92         ]
93     config_path = os.path.join(os.path.expanduser('~'), '.bash_profile')
94     with open(config_path, 'a') as f:
95         f.write('\n'.join(lines))
96
97
98 def make_posix_path(windows_path):
99     """Convert a Windows path to a posix path"""
100     for regex, sub in [
101             (re.compile(r'\\'), '/'),
102             (re.compile('^[Cc]:'), '/c'),
103             ]:
104         windows_path = regex.sub(sub, windows_path)
105     return windows_path
106
107
108 def main():
109     swc_dir = os.path.join(os.path.expanduser('~'), '.swc')
110     bin_dir = os.path.join(swc_dir, 'bin')
111     create_nosetests_entry_point(python_scripts_directory=bin_dir)
112     nano_dir = os.path.join(swc_dir, 'lib', 'nano')
113     install_nano(install_directory=nano_dir)
114     update_bash_profile(extra_paths=(nano_dir, bin_dir))
115
116
117 if __name__ == '__main__':
118     main()