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
|
from typing import List, Dict, Optional, TYPE_CHECKING, Any
from ..lib import menu
from archinstall.lib.output import log, FormattedOutput
from archinstall.lib.profile.profiles_handler import profile_handler
from archinstall.default_profiles.profile import Profile, ProfileType, SelectResult, ProfileInfo, TProfile
if TYPE_CHECKING:
from archinstall.lib.installer import Installer
_: Any
class CustomProfileList(menu.ListManager):
def __init__(self, prompt: str, profiles: List[TProfile]):
self._actions = [
str(_('Add profile')),
str(_('Edit profile')),
str(_('Delete profile'))
]
super().__init__(prompt, profiles, [self._actions[0]], self._actions[1:])
def reformat(self, data: List[TProfile]) -> Dict[str, Optional[TProfile]]:
table = FormattedOutput.as_table(data)
rows = table.split('\n')
# these are the header rows of the table and do not map to any profile obviously
# we're adding 2 spaces as prefix because the menu selector '> ' will be put before
# the selectable rows so the header has to be aligned
display_data: Dict[str, Optional[TProfile]] = {f' {rows[0]}': None, f' {rows[1]}': None}
for row, profile in zip(rows[2:], data):
row = row.replace('|', '\\|')
display_data[row] = profile
return display_data
def selected_action_display(self, profile: TProfile) -> str:
return profile.name
def handle_action(
self,
action: str,
entry: Optional['CustomTypeProfile'],
data: List['CustomTypeProfile']
) -> List['CustomTypeProfile']:
if action == self._actions[0]: # add
new_profile = self._add_profile()
if new_profile is not None:
# in case a profile with the same name as an existing profile
# was created we'll replace the existing one
data = [d for d in data if d.name != new_profile.name]
data += [new_profile]
elif entry is not None:
if action == self._actions[1]: # edit
new_profile = self._add_profile(entry)
if new_profile is not None:
# we'll remove the original profile and add the modified version
data = [d for d in data if d.name != entry.name and d.name != new_profile.name]
data += [new_profile]
elif action == self._actions[2]: # delete
data = [d for d in data if d != entry]
return data
def _is_new_profile_name(self, name: str) -> bool:
existing_profile = profile_handler.get_profile_by_name(name)
if existing_profile is not None and existing_profile.profile_type != ProfileType.CustomType:
return False
return True
def _add_profile(self, editing: Optional['CustomTypeProfile'] = None) -> Optional['CustomTypeProfile']:
name_prompt = '\n\n' + str(_('Profile name: '))
while True:
profile_name = menu.TextInput(name_prompt, editing.name if editing else '').run().strip()
if not profile_name:
return None
if not self._is_new_profile_name(profile_name):
error_prompt = str(_("The profile name you entered is already in use. Try again"))
print(error_prompt)
else:
break
packages_prompt = str(_('Packages to be install with this profile (space separated, leave blank to skip): '))
edit_packages = ' '.join(editing.packages) if editing else ''
packages = menu.TextInput(packages_prompt, edit_packages).run().strip()
services_prompt = str(_('Services to be enabled with this profile (space separated, leave blank to skip): '))
edit_services = ' '.join(editing.services) if editing else ''
services = menu.TextInput(services_prompt, edit_services).run().strip()
choice = menu.Menu(
str(_('Should this profile be enabled for installation?')),
menu.Menu.yes_no(),
skip=False,
default_option=menu.Menu.no(),
clear_screen=False,
show_search_hint=False
).run()
enable_profile = True if choice.value == menu.Menu.yes() else False
profile = CustomTypeProfile(
profile_name,
enabled=enable_profile,
packages=packages.split(' '),
services=services.split(' ')
)
return profile
# TODO
# Still needs some ironing out
class CustomProfile():
def __init__(self):
super().__init__(
'Custom',
ProfileType.Custom,
description=str(_('Create your own'))
)
def json(self) -> Dict[str, Any]:
data: Dict[str, Any] = {'main': self.name, 'gfx_driver': self.gfx_driver, 'custom': []}
for profile in self._current_selection:
data['custom'].append({
'name': profile.name,
'packages': profile.packages,
'services': profile.services,
'enabled': profile.custom_enabled
})
return data
def do_on_select(self) -> SelectResult:
custom_profile_list = CustomProfileList('', profile_handler.get_custom_profiles())
custom_profiles = custom_profile_list.run()
# we'll first remove existing custom default_profiles with
# the same name and then add the new ones this
# will avoid errors of default_profiles with duplicate naming
profile_handler.remove_custom_profiles(custom_profiles)
profile_handler.add_custom_profiles(custom_profiles)
self.set_current_selection(custom_profiles)
if custom_profile_list.is_last_choice_cancel():
return SelectResult.SameSelection
enabled_profiles = [p for p in self._current_selection if p.custom_enabled]
# in case we only created inactive default_profiles we wanna store them but
# we want to reset the original setting
if not enabled_profiles:
return SelectResult.ResetCurrent
return SelectResult.NewSelection
def post_install(self, install_session: 'Installer'):
for profile in self._current_selection:
profile.post_install(install_session)
def install(self, install_session: 'Installer'):
driver_packages = self.gfx_driver_packages()
install_session.add_additional_packages(driver_packages)
for profile in self._current_selection:
if profile.custom_enabled:
log(f'Installing custom profile {profile.name}...')
install_session.add_additional_packages(profile.packages)
install_session.enable_service(profile.services)
profile.install(install_session)
def info(self) -> Optional[ProfileInfo]:
enabled_profiles = [p for p in self._current_selection if p.custom_enabled]
if enabled_profiles:
details = ', '.join([p.name for p in enabled_profiles])
gfx_driver = self.gfx_driver
return ProfileInfo(self.name, details, gfx_driver)
return None
def reset(self):
for profile in self._current_selection:
profile.set_enabled(False)
self.gfx_driver = None
class CustomTypeProfile(Profile):
def __init__(
self,
name: str,
enabled: bool = False,
packages: List[str] = [],
services: List[str] = []
):
super().__init__(
name,
ProfileType.CustomType,
packages=packages,
services=services,
support_gfx_driver=True
)
self.custom_enabled = enabled
def json(self) -> Dict[str, Any]:
return {
'name': self.name,
'packages': self.packages,
'services': self.services,
'enabled': self.custom_enabled
}
|