Merge pull request #27 from makermelissa/main
New features required for installer script changes
This commit is contained in:
commit
7def5108ea
1 changed files with 110 additions and 5 deletions
|
|
@ -23,6 +23,7 @@ Implementation Notes
|
||||||
# imports
|
# imports
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
import stat
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
@ -50,10 +51,28 @@ RASPI_VERSIONS = (
|
||||||
|
|
||||||
WINDOW_MANAGERS = {
|
WINDOW_MANAGERS = {
|
||||||
"x11": "W1",
|
"x11": "W1",
|
||||||
"wayland": "W2",
|
"wayfire": "W2",
|
||||||
"labwc": "W3",
|
"labwc": "W3",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FILE_MODES = {
|
||||||
|
"+x": stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
|
||||||
|
"+r": stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH,
|
||||||
|
"+w": stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH,
|
||||||
|
"a+x": stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH,
|
||||||
|
"a+r": stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH,
|
||||||
|
"a+w": stat.S_IWUSR | stat.S_IWGRP | stat.S_IWOTH,
|
||||||
|
"u+x": stat.S_IXUSR,
|
||||||
|
"u+r": stat.S_IRUSR,
|
||||||
|
"u+w": stat.S_IWUSR,
|
||||||
|
"g+x": stat.S_IXGRP,
|
||||||
|
"g+r": stat.S_IRGRP,
|
||||||
|
"g+w": stat.S_IWGRP,
|
||||||
|
"o+x": stat.S_IXOTH,
|
||||||
|
"o+r": stat.S_IROTH,
|
||||||
|
"o+w": stat.S_IWOTH,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
class Shell:
|
class Shell:
|
||||||
|
|
@ -142,6 +161,50 @@ class Shell:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def write_templated_file(self, output_path, template, **kwargs):
|
||||||
|
"""
|
||||||
|
Use a template file and render it with the given context and write it to the specified path.
|
||||||
|
The template file should contain placeholders in the format {key} which will be replaced
|
||||||
|
with the corresponding values from the kwargs dictionary.
|
||||||
|
"""
|
||||||
|
# if path is an existing directory, the template filename will be used
|
||||||
|
output_path = self.path(output_path)
|
||||||
|
if os.path.isdir(output_path):
|
||||||
|
output_path = os.path.join(output_path, os.path.basename(template))
|
||||||
|
|
||||||
|
# Render the template with the provided context
|
||||||
|
rendered_content = self.load_template(template, **kwargs)
|
||||||
|
|
||||||
|
if rendered_content is None:
|
||||||
|
self.error(
|
||||||
|
f"Failed to load template '{template}'. Unable to write file '{output_path}'."
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
append = kwargs.get("append", False)
|
||||||
|
self.write_text_file(output_path, rendered_content, append=append)
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def load_template(self, template, **kwargs):
|
||||||
|
"""
|
||||||
|
Load a template file and return its content with the placeholders replaced by the provided
|
||||||
|
context. The template file should contain placeholders in the format {key} which will be
|
||||||
|
replaced with the corresponding values from the kwargs dictionary.
|
||||||
|
"""
|
||||||
|
if not os.path.exists(template):
|
||||||
|
self.error(f"Template file '{template}' does not exist")
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(template, "r") as template_file:
|
||||||
|
template_content = template_file.read()
|
||||||
|
|
||||||
|
# Render the template with the provided context
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
template_content = template_content.replace(f"{{{key}}}", str(value))
|
||||||
|
|
||||||
|
return template_content
|
||||||
|
|
||||||
def info(self, message, **kwargs):
|
def info(self, message, **kwargs):
|
||||||
"""
|
"""
|
||||||
Display a message with the group in green
|
Display a message with the group in green
|
||||||
|
|
@ -321,7 +384,10 @@ class Shell:
|
||||||
# Not found; append (silently)
|
# Not found; append (silently)
|
||||||
self.write_text_file(file, replacement, append=True)
|
self.write_text_file(file, replacement, append=True)
|
||||||
|
|
||||||
def pattern_search(self, location, pattern, multi_line=False, return_match=False):
|
# pylint: disable=too-many-arguments
|
||||||
|
def pattern_search(
|
||||||
|
self, location, pattern, multi_line=False, return_match=False, find_all=False
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Similar to grep, but uses pure python
|
Similar to grep, but uses pure python
|
||||||
multi_line will search the entire file as a large text glob,
|
multi_line will search the entire file as a large text glob,
|
||||||
|
|
@ -331,16 +397,17 @@ class Shell:
|
||||||
"""
|
"""
|
||||||
location = self.path(location)
|
location = self.path(location)
|
||||||
found = False
|
found = False
|
||||||
|
search_function = re.findall if find_all else re.search
|
||||||
|
|
||||||
if self.exists(location) and not self.isdir(location):
|
if self.exists(location) and not self.isdir(location):
|
||||||
if multi_line:
|
if multi_line:
|
||||||
with open(location, "r+", encoding="utf-8") as file:
|
with open(location, "r+", encoding="utf-8") as file:
|
||||||
match = re.search(pattern, file.read(), flags=re.DOTALL)
|
match = search_function(pattern, file.read(), flags=re.DOTALL)
|
||||||
if match:
|
if match:
|
||||||
found = True
|
found = True
|
||||||
else:
|
else:
|
||||||
for line in fileinput.FileInput(location):
|
for line in fileinput.FileInput(location):
|
||||||
match = re.search(pattern, line)
|
match = search_function(pattern, line)
|
||||||
if match:
|
if match:
|
||||||
found = True
|
found = True
|
||||||
break
|
break
|
||||||
|
|
@ -417,8 +484,13 @@ class Shell:
|
||||||
Change the permissions of a file or directory
|
Change the permissions of a file or directory
|
||||||
"""
|
"""
|
||||||
location = self.path(location)
|
location = self.path(location)
|
||||||
|
# Convert a text mode to an integer mode
|
||||||
|
if isinstance(mode, str):
|
||||||
|
if mode not in FILE_MODES:
|
||||||
|
raise ValueError(f"Invalid mode string '{mode}'")
|
||||||
|
mode = FILE_MODES[mode]
|
||||||
if not 0 <= mode <= 0o777:
|
if not 0 <= mode <= 0o777:
|
||||||
raise ValueError("Invalid mode value")
|
raise ValueError(f"Invalid mode value '{mode}'")
|
||||||
if os.path.exists(location):
|
if os.path.exists(location):
|
||||||
os.chmod(location, mode)
|
os.chmod(location, mode)
|
||||||
|
|
||||||
|
|
@ -475,6 +547,16 @@ class Shell:
|
||||||
with open(self.path(path), mode, encoding="utf-8") as service_file:
|
with open(self.path(path), mode, encoding="utf-8") as service_file:
|
||||||
service_file.write(content)
|
service_file.write(content)
|
||||||
|
|
||||||
|
def read_text_file(self, path):
|
||||||
|
"""
|
||||||
|
Read the contents of a file at the specified path
|
||||||
|
"""
|
||||||
|
path = self.path(path)
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise FileNotFoundError(f"File '{path}' does not exist")
|
||||||
|
with open(path, "r", encoding="utf-8") as file:
|
||||||
|
return file.read()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_python3():
|
def is_python3():
|
||||||
"Check if we are running Python 3 or later"
|
"Check if we are running Python 3 or later"
|
||||||
|
|
@ -656,6 +738,29 @@ class Shell:
|
||||||
):
|
):
|
||||||
raise RuntimeError("Unable to change window manager")
|
raise RuntimeError("Unable to change window manager")
|
||||||
|
|
||||||
|
def get_window_manager(self):
|
||||||
|
"""
|
||||||
|
Get the current window manager
|
||||||
|
"""
|
||||||
|
sessions = {"wayfire": "LXDE-pi-wayfire"}
|
||||||
|
# Check for Raspbian Desktop sessions
|
||||||
|
if self.exists("/usr/share/xsessions/rpd-x.desktop") or self.exists(
|
||||||
|
"/usr/share/wayland-sessions/rpd-labwc.desktop"
|
||||||
|
):
|
||||||
|
sessions.update({"x11": "rpd-x", "labwc": "rpd-labwc"})
|
||||||
|
else:
|
||||||
|
sessions.update({"x11": "LXDE-pi-x", "labwc": "LXDE-pi-labwc"})
|
||||||
|
|
||||||
|
matches = self.pattern_search(
|
||||||
|
"/etc/lightdm/lightdm.conf", "^(?!#.*?)user-session=(.+)", False, True
|
||||||
|
)
|
||||||
|
if matches:
|
||||||
|
session_match = matches.group(1)
|
||||||
|
for key, session in sessions.items():
|
||||||
|
if session_match == session:
|
||||||
|
return key
|
||||||
|
return None
|
||||||
|
|
||||||
def get_boot_config(self):
|
def get_boot_config(self):
|
||||||
"""
|
"""
|
||||||
Get the location of the boot config file
|
Get the location of the boot config file
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue