Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Hvornum <anton.feeds+github@gmail.com>2020-10-18 21:24:46 +0200
committerGitHub <noreply@github.com>2020-10-18 21:24:46 +0200
commitf32f1e238a0a36b0d1b5220df6f6f053c6c4168c (patch)
tree78053ae908629bb49725246fcb2e957875d9ec3b
parent3ba6e69e051e4614ebf18f7f08c0d7a18366d9b7 (diff)
parent06ee896c6c58e877325896b33a2a3dd3eb0a1727 (diff)
Merge pull request #55 from Torxed/43-overview
Added a configuration overview before formatting
-rw-r--r--archinstall/lib/disk.py10
-rw-r--r--archinstall/lib/general.py38
-rw-r--r--archinstall/lib/profiles.py3
-rw-r--r--examples/guided.py87
4 files changed, 118 insertions, 20 deletions
diff --git a/archinstall/lib/disk.py b/archinstall/lib/disk.py
index 7cd8dd0f..b11f2318 100644
--- a/archinstall/lib/disk.py
+++ b/archinstall/lib/disk.py
@@ -25,6 +25,16 @@ class BlockDevice():
raise KeyError(f'{self} does not contain information: "{key}"')
return self.info[key]
+ def json(self):
+ """
+ json() has precedence over __dump__, so this is a way
+ to give less/partial information for user readability.
+ """
+ return {
+ 'path' : self.path,
+ 'size' : self.info['size'] if 'size' in self.info else '<unknown>'
+ }
+
def __dump__(self):
return {
'path' : self.path,
diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py
index 393bf69a..ff834241 100644
--- a/archinstall/lib/general.py
+++ b/archinstall/lib/general.py
@@ -1,5 +1,6 @@
import os, json, hashlib, shlex, sys
import time, pty
+from datetime import datetime, date
from subprocess import Popen, STDOUT, PIPE, check_output
from select import epoll, EPOLLIN, EPOLLHUP
from .exceptions import *
@@ -30,6 +31,43 @@ def locate_binary(name):
return os.path.join(root, file)
break # Don't recurse
+class JSON_Encoder:
+ def _encode(obj):
+ if isinstance(obj, dict):
+ ## We'll need to iterate not just the value that default() usually gets passed
+ ## But also iterate manually over each key: value pair in order to trap the keys.
+
+ for key, val in list(obj.items()):
+ if isinstance(val, dict):
+ val = json.loads(json.dumps(val, cls=JSON)) # This, is a EXTREMELY ugly hack..
+ # But it's the only quick way I can think of to
+ # trigger a encoding of sub-dictionaries.
+ else:
+ val = JSON_Encoder._encode(val)
+ del(obj[key])
+ obj[JSON_Encoder._encode(key)] = val
+ return obj
+ elif hasattr(obj, 'json'):
+ return obj.json()
+ elif hasattr(obj, '__dump__'):
+ return obj.__dump__()
+ elif isinstance(obj, (datetime, date)):
+ return obj.isoformat()
+ elif isinstance(obj, (list, set, tuple)):
+ r = []
+ for item in obj:
+ r.append(json.loads(json.dumps(item, cls=JSON)))
+ return r
+ else:
+ return obj
+
+class JSON(json.JSONEncoder, json.JSONDecoder):
+ def _encode(self, obj):
+ return JSON_Encoder._encode(obj)
+
+ def encode(self, obj):
+ return super(JSON, self).encode(self._encode(obj))
+
class sys_command():#Thread):
"""
Stolen from archinstall_gui
diff --git a/archinstall/lib/profiles.py b/archinstall/lib/profiles.py
index 47e6dd36..e018f753 100644
--- a/archinstall/lib/profiles.py
+++ b/archinstall/lib/profiles.py
@@ -61,6 +61,9 @@ class Profile():
self._cache = None
self.args = args
+ def __dump__(self, *args, **kwargs):
+ return {'path' : self._path}
+
def __repr__(self, *args, **kwargs):
return f'Profile({self._path} <"{self.path}">)'
diff --git a/examples/guided.py b/examples/guided.py
index 30514a71..cc660b90 100644
--- a/examples/guided.py
+++ b/examples/guided.py
@@ -1,4 +1,20 @@
-import archinstall, getpass, time
+import archinstall
+import getpass, time, json, sys, signal
+
+"""
+This signal-handler chain (and global variable)
+is used to trigger the "Are you sure you want to abort?" question.
+"""
+SIG_TRIGGER = False
+def kill_handler(sig, frame):
+ print()
+ exit(0)
+def sig_handler(sig, frame):
+ global SIG_TRIGGER
+ SIG_TRIGGER = True
+ signal.signal(signal.SIGINT, kill_handler)
+original_sigint_handler = signal.getsignal(signal.SIGINT)
+signal.signal(signal.SIGINT, sig_handler)
def perform_installation(device, boot_partition, language, mirrors):
"""
@@ -50,8 +66,13 @@ archinstall.sys_command(f'cryptsetup close /dev/mapper/luksloop', surpress_error
keyboard_language = archinstall.select_language(archinstall.list_keyboard_languages())
archinstall.set_keyboard_language(keyboard_language)
+# Create a storage structure for all our information.
+# We'll print this right before the user gets informed about the formatting timer.
+archinstall.storage['_guided'] = {}
+
# Set which region to download packages from during the installation
mirror_regions = archinstall.select_mirror_regions(archinstall.list_mirrors())
+archinstall.storage['_guided']['mirrors'] = mirror_regions
# Ask which harddrive/block-device we will install to
harddrive = archinstall.select_disk(archinstall.all_disks())
@@ -61,10 +82,12 @@ while (disk_password := getpass.getpass(prompt='Enter disk encryption password (
archinstall.log(' * Passwords did not match * ', bg='black', fg='red')
continue
break
+archinstall.storage['_guided']['harddrive'] = harddrive
# Ask for a hostname
hostname = input('Desired hostname for the installation: ')
if len(hostname) == 0: hostname = 'ArchInstall'
+archinstall.storage['_guided']['hostname'] = hostname
# Ask for a root password (optional, but triggers requirement for super-user if skipped)
while (root_pw := getpass.getpass(prompt='Enter root password (leave blank to leave root disabled): ')):
@@ -87,6 +110,10 @@ while 1:
archinstall.log(' * Since root is disabled, you need to create a least one (super) user!', bg='black', fg='red')
continue
break
+
+ if 'users' not in archinstall.storage['_guided']: archinstall.storage['_guided']['users'] = []
+ archinstall.storage['_guided']['users'].append(new_user)
+
new_user_passwd = getpass.getpass(prompt=f'Password for user {new_user}: ')
new_user_passwd_verify = getpass.getpass(prompt=f'Enter password again for verification: ')
if new_user_passwd != new_user_passwd_verify:
@@ -99,12 +126,17 @@ while 1:
# Ask for archinstall-specific profiles (such as desktop environments etc)
while 1:
profile = archinstall.select_profile(archinstall.list_profiles())
- if profile and type(profile) != str: # Got a imported profile
- if not profile[1]._prep_function():
- archinstall.log(' * Profile\'s preperation requirements was not fulfilled.', bg='black', fg='red')
- continue
- profile = profile[0]._path # Once the prep is done, replace the selected profile with the profile name ("path") given from select_profile()
- break
+ if profile:
+ archinstall.storage['_guided']['profile'] = profile
+
+ if type(profile) != str: # Got a imported profile
+ archinstall.storage['_guided']['profile'] = profile[0] # The second return is a module, and not a handle/object.
+ if not profile[1]._prep_function():
+ archinstall.log(' * Profile\'s preperation requirements was not fulfilled.', bg='black', fg='red')
+ continue
+
+ profile = profile[0]._path # Once the prep is done, replace the selected profile with the profile name ("path") given from select_profile()
+ break
else:
break
@@ -117,27 +149,42 @@ while 1:
try:
if archinstall.validate_package_list(packages):
+ archinstall.storage['_guided']['packages'] = packages
break
except archinstall.RequirementError as e:
print(e)
-# TODO: Print a summary here of all the options chosen.
-# Ideally, archinstall should keep track of this internally
-# and there should be something like print(archinstall.config).
+print()
+print('This is your chosen configuration:')
+print(json.dumps(archinstall.storage['_guided'], indent=4, sort_keys=True, cls=archinstall.JSON))
+print()
"""
Issue a final warning before we continue with something un-revertable.
+ We mention the drive one last time, and count from 5 to 0.
"""
-print(f' ! Formatting {harddrive} in 5...')
-time.sleep(1)
-print(f' ! Formatting {harddrive} in 4...')
-time.sleep(1)
-print(f' ! Formatting {harddrive} in 3...')
-time.sleep(1)
-print(f' ! Formatting {harddrive} in 2...')
-time.sleep(1)
-print(f' ! Formatting {harddrive} in 1...')
-time.sleep(1)
+
+print(f' ! Formatting {harddrive} in ', end='')
+
+for i in range(5, 0, -1):
+ print(f"{i}", end='')
+
+ for x in range(4):
+ sys.stdout.flush()
+ time.sleep(0.25)
+ print(".", end='')
+
+ if SIG_TRIGGER:
+ abort = input('\nDo you really want to abort (y/n)? ')
+ if abort.strip() != 'n':
+ exit(0)
+
+ if SIG_TRIGGER is False:
+ sys.stdin.read()
+ SIG_TRIGGER = False
+ signal.signal(signal.SIGINT, sig_handler)
+print()
+signal.signal(signal.SIGINT, original_sigint_handler)
"""
Setup the blockdevice, filesystem (and optionally encryption).