-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsetup.py
215 lines (164 loc) · 6.67 KB
/
setup.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
import os
import subprocess
import argparse
import sys
from typing import Iterable, Union, Dict
DOTFILES_DIR = os.path.abspath('./dotfiles')
HOME_DIR = os.path.expanduser('~')
def echo_run(command):
print(command)
return subprocess.run(command.split())
class Dependency:
def install(self):
pass
# For dependencies managed by the system package manager
class SystemDependency(Dependency):
def __init__(self, dependency_name_by_os):
if not dependency_name_by_os:
raise ValueError("Dependencies must have a name on at least one operating system")
if isinstance(dependency_name_by_os, str):
self._name_by_os = {
sys.platform: dependency_name_by_os
}
elif isinstance(dependency_name_by_os, dict):
self._name_by_os = dependency_name_by_os
else:
raise TypeError()
canonical_os = next(iter(self._name_by_os))
self._canonical_name = self._name_by_os[canonical_os]
self._install_commands = {
"darwin": "brew install",
"linux": "sudo apt-get install"
}
def install(self):
if sys.platform not in self._install_commands:
raise NotImplementedError(f"Installing system dependencies for {sys.platform} is not configured.")
install = self._install_commands[sys.platform]
if sys.platform not in self._name_by_os:
raise ValueError(f"Dependency {self._canonical_name} has no install name on {sys.platform}")
name = self._name_by_os[sys.platform]
echo_run(f"{install} {name}")
class CustomSystemDependency(Dependency):
def __init__(self, canonical_name, installation_commands_by_os):
self._canonical_name = canonical_name
self._install_commands = installation_commands_by_os
def install(self):
if sys.platform not in self._install_commands:
raise ValueError(f"Dependency {self._canonical_name} has no install name on {sys.platform}")
for command in self._install_commands[sys.platform]:
echo_run(command)
class Homebrew(Dependency):
# TODO: Implement
pass
class Conda(Dependency):
def __init__(self):
conda_platforms = {
"darwin": "MacOSX",
"linux": "Linux",
}
if sys.platform not in conda_platforms:
raise NotImplementedError(f"Installing conda for {sys.platform} is not configured.")
conda_platform = conda_platforms[sys.platform]
self._install_script_url = f"https://repo.anaconda.com/miniconda/Miniconda3-latest-{conda_platform}-x86_64.pkg"
self._install_target = "/tmp/install_conda.sh"
def install(self):
if self._is_installed(): # check to see if conda is already installed
print("Installing conda...")
subprocess.run(f"curl {self._install_script_url} --output {self._install_target}")
subprocess.run(self._install_target)
def _is_installed(self):
run_conda = subprocess.run(
"conda",
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
return run_conda.returncode != 0
class Mamba(Dependency):
def install(self):
echo_run("conda install mamba -n base -c conda-forge")
class Nvm(Dependency):
def install(self):
# TODO: Fix this!!
# Add these lines to ~/.zshrc on mac:
# export NVM_DIR="$HOME/.nvm"
# [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
# [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
echo_run("curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.38.0/install.sh | bash")
DEPENDENCIES: Iterable[Dependency] = [
# Package managers
Homebrew(),
Conda(),
Mamba(),
Nvm(),
SystemDependency('pass'),
SystemDependency('pandoc'),
SystemDependency('htop'),
CustomSystemDependency("git-credential-manager", {
"darwin": [
"brew tap microsoft/git",
"brew install --cask git-credential-manager-core",
],
}),
# Graphics
CustomSystemDependency('gimp', {
"darwin": [
"brew install --cask gimp",
],
}),
SystemDependency('imagemagick'),
]
def link_dotfiles():
home_files = os.listdir(HOME_DIR)
for config_file in os.listdir(DOTFILES_DIR):
if config_file not in home_files:
print(f"Linking {config_file}")
os.symlink(
os.path.join(DOTFILES_DIR, config_file),
os.path.join(HOME_DIR, config_file)
)
def setup_git(email: str):
user_info = subprocess.check_output(['finger', '$(whoami)'], shell=True, encoding='utf8').split()
first_name, last_name = user_info[9:11]
full_name = " ".join([first_name, last_name])
echo_run(f"git config --global user.name {full_name}")
echo_run(f"git config --global user.email {email}")
echo_run(f"git config --global init.defaultBranch main")
def setup_pass():
print("In order to set up 'pass', this script needs the appropriate credentials to be present.")
print(
"""
Specifically: (1) GPG keys used to encrypt passwords for 'pass' must be placed in ~/.gnupg.
(2) GPG-encrypted files containing passwords must be populated in ~/.password-store.
"""
)
def skip_pass_setup():
continue_options = ["c", "continue"]
skip_options = ["s", "skip"]
selection = input("Would you like to continue, or skip and come back later? [c]ontinue or [s]kip: ")
if selection not in continue_options + skip_options:
return get_selection()
return selection in skip_options
if skip_pass_setup():
return
# Set proper permissions on ~/.gnupg directory.
for dirpath, dirnames, filenames in os.walk(os.path.expanduser("~/.gnupg")):
for dirname in dirnames:
os.chmod(os.path.join(dirpath, dirname), 0o700)
for filename in filenames:
os.chmod(os.path.join(dirpath, filename), 0o600)
# So pass doesn't print '.gpg' at the end of every password filename.
for dirpath, dirnames, filenames in os.walk(os.path.expanduser("~/.password-store")):
for filename in filenames:
os.chmod(os.path.join(dirpath, filename), 0o600)
def main():
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--email', help='Email to associate with this user profile', required=True)
args = parser.parse_args()
# All commands should be idempotent
link_dotfiles()
setup_git(args.email)
for dependency in DEPENDENCIES:
dependency.install()
setup_pass()
if __name__ == '__main__':
main()