-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdotphile
executable file
·125 lines (100 loc) · 3.81 KB
/
dotphile
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
#!/usr/bin/env python3
# Author: Jon Smithers <[email protected]>
# URL: https://github.com/jonsmithers/dotfiles/blob/master/dotphile
# Last Updated: 2023-09-25
import filecmp
import json
import os
import subprocess
import sys
from enum import Enum
class BColors:
HEADER = '\033[95m'
OK_BLUE = '\033[94m'
OK_GREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
NORMAL = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
class LinkState(Enum):
LINKED = 1
UNLINKED = 2
BLOCKED = 3
INVALID = 4
# NOTE: broken symlinks are a weird case and aren't properly reported
class SymlinkMapping:
def __init__(self, local_path, operative_path):
self.local_path = local_path
self.operative_path = operative_path
self.state: LinkState = self.compute_link_state()
def __str__(self):
return f"SymlinkMapping({self.local_path} --> {self.operative_path})"
def compute_link_state(self) -> LinkState:
# local_path can be a file or a directory
if not os.path.exists(self.operative_path):
return LinkState.UNLINKED
if os.path.islink(self.operative_path) and os.path.realpath(self.operative_path) == self.local_path:
return LinkState.LINKED
return LinkState.BLOCKED
symlink_mappings = []
for _local_path, _operative_path in list(json.load(open("config.json"))["links"].items()):
symlink_mappings.append(
SymlinkMapping(
local_path=os.path.realpath(_local_path),
operative_path=os.path.expanduser(_operative_path)
)
)
missing_local_paths = [pair.local_path for pair in symlink_mappings if not os.path.exists(pair.local_path)]
if len(missing_local_paths):
exit("Some files are missing\n " + "\n ".join(missing_local_paths))
linked_mappings = [m for m in symlink_mappings if m.state == LinkState.LINKED]
blocked_mappings = [m for m in symlink_mappings if m.state == LinkState.BLOCKED]
unlinked_mappings = [m for m in symlink_mappings if m.state == LinkState.UNLINKED]
bash_commands = [(
f"mkdir -p {os.path.dirname(m.operative_path)}",
f"ln -s '{m.local_path}' '{m.operative_path}'",
) for m in unlinked_mappings]
if len(linked_mappings):
print()
print(str(len(linked_mappings)) + " paths are already linked")
print(BColors.OK_BLUE + " " + "\n ".join(
[m.operative_path for m in linked_mappings]) + BColors.NORMAL)
if len(blocked_mappings):
print()
print(BColors.BOLD + BColors.FAIL + str(
len(blocked_mappings)) + " paths can't be linked because the operative path already exists" + BColors.NORMAL)
print(BColors.FAIL + " " + "\n ".join(
[m.operative_path for m in blocked_mappings]) + BColors.NORMAL)
if not len(unlinked_mappings):
print()
exit(len(blocked_mappings))
print("\n" + BColors.BOLD + str(len(unlinked_mappings)) + " paths will be linked" + BColors.NORMAL)
print(BColors.OK_GREEN + "".join([" " + _symlink_cmd for (_, _symlink_cmd) in bash_commands]) + BColors.NORMAL)
sys.stdout.write("\nOk to create " + str(len(unlinked_mappings)) + " symlinks? (y/n) ")
result = input()
if result == 'filter':
print('hm')
exit()
elif result == 'y':
success_count = 0
fail_count = 0
print(BColors.FAIL, end=' ')
def run(s):
return not subprocess.run(["bash", "-c", s]).returncode
for mkdir_cmd, symlink_cmd in bash_commands:
if run(mkdir_cmd) and run(symlink_cmd):
success_count += 1
else:
fail_count += 1
print(BColors.NORMAL, end=' ')
if success_count:
print()
print(str(success_count) + " links succeeded")
if fail_count:
print()
print(BColors.FAIL + str(fail_count) + " LINKS FAILED!" + BColors.NORMAL)
exit(1)
exit(0)
else:
print("\nI was really hoping you'd say \"y\".")