Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 043b2618a3 | |||
| c264a5bea2 | |||
| bb123c23a1 | |||
| 414c065df4 | |||
| 35c976fcac |
Regular → Executable
+3
@@ -1,3 +1,6 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# PYTHON_ARGCOMPLETE_OK
|
||||||
|
|
||||||
import cli.args as cliargs
|
import cli.args as cliargs
|
||||||
|
|
||||||
import commands.commit as commit
|
import commands.commit as commit
|
||||||
|
|||||||
+2
-1
@@ -26,7 +26,8 @@ def add_cli_args():
|
|||||||
"--no-render", "-r", help="do not re-render renderables", action="store_true"
|
"--no-render", "-r", help="do not re-render renderables", action="store_true"
|
||||||
)
|
)
|
||||||
|
|
||||||
sp.add_parser("config", help="prints information about your config")
|
# TODO: Allow changing things like config path
|
||||||
|
sp.add_parser("config", help="prints information about your config and change some of them")
|
||||||
|
|
||||||
sp.add_parser("setup", help="Do initial setup, like installing required tools")
|
sp.add_parser("setup", help="Do initial setup, like installing required tools")
|
||||||
|
|
||||||
|
|||||||
+11
-65
@@ -1,75 +1,21 @@
|
|||||||
from typing import List
|
|
||||||
from commands.util import pacman
|
from commands.util import pacman
|
||||||
|
from commands.util.diff import pkg_diff
|
||||||
from commands.util.input_mgr import confirm, password
|
from commands.util.input_mgr import confirm, password
|
||||||
import colorama as cl
|
|
||||||
|
|
||||||
from commands.util.printer import print_list
|
from commands.util.printing.diff import print_diff
|
||||||
|
|
||||||
|
|
||||||
def commit(force: bool = False, no_render: bool = False):
|
def commit(force: bool = False, no_render: bool = False):
|
||||||
print("Commit, force:", force)
|
"""Commit the changes to the system
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force: Apply, overriding any changes since the last commit without confirming
|
||||||
|
no_render: Don't rerender the templates (use cached version).
|
||||||
|
Will be ignored if cache is unavailable (and prompt the user)
|
||||||
|
"""
|
||||||
# TODO: Make sure we don't uninstall critical system packages by accident (i.e. prompt user)
|
# TODO: Make sure we don't uninstall critical system packages by accident (i.e. prompt user)
|
||||||
# Probably do that check in the pacman util lib tho
|
# Probably do that check in the pacman util lib tho
|
||||||
print_diff(pacman.list_explicitly_installed(), [])
|
add, remove = pkg_diff([], pacman.list_explicitly_installed())
|
||||||
|
print_diff(add, remove)
|
||||||
if confirm(True, "Do you really want to proceed?"):
|
if confirm(True, "Do you really want to proceed?"):
|
||||||
pw = password()
|
pw = password()
|
||||||
|
|
||||||
|
|
||||||
def print_diff(add: List[str], remove: List[str]):
|
|
||||||
if len(add) == 0 and len(remove) == 0:
|
|
||||||
print(
|
|
||||||
cl.Fore.BLUE
|
|
||||||
+ "-->"
|
|
||||||
+ cl.Style.DIM
|
|
||||||
+ cl.Fore.GREEN
|
|
||||||
+ " No packages changes"
|
|
||||||
+ cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
# Packages to be installed
|
|
||||||
if len(add) == 0:
|
|
||||||
print(
|
|
||||||
cl.Fore.BLUE
|
|
||||||
+ "-->"
|
|
||||||
+ cl.Style.DIM
|
|
||||||
+ cl.Fore.GREEN
|
|
||||||
+ " No packages to be installed"
|
|
||||||
+ cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print(
|
|
||||||
cl.Fore.GREEN + "==>",
|
|
||||||
cl.Style.RESET_ALL + "Packages that will be",
|
|
||||||
cl.Fore.GREEN + "installed" + cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
print_list(add)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Packages to be removed
|
|
||||||
if len(remove) == 0:
|
|
||||||
print(
|
|
||||||
cl.Fore.BLUE
|
|
||||||
+ "-->"
|
|
||||||
+ cl.Style.DIM
|
|
||||||
+ cl.Fore.GREEN
|
|
||||||
+ " No packages to be uninstalled"
|
|
||||||
+ cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
print(
|
|
||||||
cl.Fore.GREEN + "==>",
|
|
||||||
cl.Style.RESET_ALL + "Packages that will be",
|
|
||||||
cl.Fore.RED + "uninstalled" + cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
print_list(remove)
|
|
||||||
print()
|
|
||||||
|
|
||||||
# Ask user to confirm
|
|
||||||
print(
|
|
||||||
cl.Fore.GREEN + "==>",
|
|
||||||
cl.Style.RESET_ALL
|
|
||||||
+ f"Transaction (packages): {cl.Fore.BLUE + str(len(add)) + cl.Style.RESET_ALL}",
|
|
||||||
cl.Fore.GREEN + "installed",
|
|
||||||
cl.Style.RESET_ALL
|
|
||||||
+ f"and {cl.Fore.BLUE + str(len(remove)) + cl.Style.RESET_ALL}",
|
|
||||||
cl.Fore.RED + "uninstalled" + cl.Style.RESET_ALL,
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
from commands.util import pacman
|
|
||||||
|
|
||||||
|
|
||||||
def config():
|
def config():
|
||||||
print("""
|
print("""
|
||||||
Your config can be found at
|
Your config can be found at
|
||||||
""")
|
""")
|
||||||
pacman.install_package_list(["chromium"])
|
|
||||||
|
|||||||
@@ -12,3 +12,8 @@ def init(force: bool = False):
|
|||||||
with open(dir + "/config.yaml") as file:
|
with open(dir + "/config.yaml") as file:
|
||||||
file.write("")
|
file.write("")
|
||||||
print("Initialized a new archmgr repository")
|
print("Initialized a new archmgr repository")
|
||||||
|
# TODO: Warn user to not delete .config/archmgr repo
|
||||||
|
# TODO: Set up that repo (where to put it? /usr/share?)
|
||||||
|
# TODO: Consider collecting function
|
||||||
|
# TODO: Config folder instead of single config file
|
||||||
|
# TODO: Also store the folder name of the config folder in that repo (needs to be easily changeable for user!)
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def pkg_diff(target: List[str], actual: List[str]) -> tuple[List[str], List[str]]:
|
||||||
|
"""Compute a diff between target packages and installed packages
|
||||||
|
|
||||||
|
Args:
|
||||||
|
target: The target packages
|
||||||
|
actual: The actually installed packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple with first the missing (not installed) packages
|
||||||
|
and second the extraneous (to be uninstalled) packages
|
||||||
|
"""
|
||||||
|
for i, pkg in enumerate(actual):
|
||||||
|
try:
|
||||||
|
idx = target.index(pkg)
|
||||||
|
target.pop(idx)
|
||||||
|
actual.pop(i)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return (target, actual)
|
||||||
+12
-18
@@ -1,7 +1,6 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
import getpass
|
|
||||||
import time
|
from commands.util.password_manager import PasswordManager
|
||||||
import colorama
|
|
||||||
|
|
||||||
|
|
||||||
def choice(default: str, options: str, msg: str) -> str:
|
def choice(default: str, options: str, msg: str) -> str:
|
||||||
@@ -33,18 +32,13 @@ def confirm_overwrite(msg: Optional[str] = ""):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def password(msg: str = ""):
|
# Use class to store the password
|
||||||
pw = ""
|
pw_manager = PasswordManager()
|
||||||
if msg != "":
|
|
||||||
pw = getpass.getpass(msg)
|
|
||||||
else:
|
def password():
|
||||||
pw = getpass.getpass()
|
return pw_manager.get()
|
||||||
if pw != "":
|
|
||||||
return pw
|
|
||||||
else:
|
def unlock_sudo():
|
||||||
time.sleep(1)
|
return pw_manager.validate()
|
||||||
print(
|
|
||||||
colorama.Fore.RED + "Error:",
|
|
||||||
colorama.Style.RESET_ALL + "Password cannot be empty",
|
|
||||||
)
|
|
||||||
return password(msg)
|
|
||||||
|
|||||||
+11
-4
@@ -1,6 +1,8 @@
|
|||||||
import subprocess as sp
|
import subprocess as sp
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
|
from commands.util.input_mgr import password
|
||||||
|
|
||||||
pkg_manager = ["yay", "--noconfirm"]
|
pkg_manager = ["yay", "--noconfirm"]
|
||||||
|
|
||||||
|
|
||||||
@@ -19,7 +21,13 @@ def remove_orphans() -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
True if successful, False otherwise
|
True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
return sp.run("pacman -Qdtq | sudo pacman -Rns --noconfirm -", capture_output=True).returncode == 0
|
return (
|
||||||
|
sp.run(
|
||||||
|
["pacman", "-Qdtq", "|", "sudo", "pacman", "-Rns", "--noconfirm", "-"],
|
||||||
|
capture_output=True,
|
||||||
|
).returncode
|
||||||
|
== 0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def uninstall_package_list(pkgs: List[str]) -> bool:
|
def uninstall_package_list(pkgs: List[str]) -> bool:
|
||||||
@@ -31,7 +39,7 @@ def uninstall_package_list(pkgs: List[str]) -> bool:
|
|||||||
Returns:
|
Returns:
|
||||||
True if successful, False otherwise
|
True if successful, False otherwise
|
||||||
"""
|
"""
|
||||||
# TODO: Add guard to protect against uninstalling archmgr
|
# TODO: Add guard to protect against uninstalling archmgr and confirm uninstalling crucial pkgs
|
||||||
# pkgs.index("archmgr")
|
# pkgs.index("archmgr")
|
||||||
# TODO: Consider if we want to print out stdout and stderr on err
|
# TODO: Consider if we want to print out stdout and stderr on err
|
||||||
return run_pkg_manager_cmd(["-Rs", *pkgs], True).returncode == 0
|
return run_pkg_manager_cmd(["-Rs", *pkgs], True).returncode == 0
|
||||||
@@ -49,9 +57,8 @@ def install_package_list(pkgs: List[str]) -> bool:
|
|||||||
def run_pkg_manager_cmd(
|
def run_pkg_manager_cmd(
|
||||||
args: List[str], use_sudo: bool = False
|
args: List[str], use_sudo: bool = False
|
||||||
) -> sp.CompletedProcess[str]:
|
) -> sp.CompletedProcess[str]:
|
||||||
# TODO: Get password
|
|
||||||
pw = ""
|
|
||||||
if use_sudo:
|
if use_sudo:
|
||||||
|
pw = password()
|
||||||
return sp.run([*pkg_manager, *args], capture_output=True, text=True, input=pw)
|
return sp.run([*pkg_manager, *args], capture_output=True, text=True, input=pw)
|
||||||
else:
|
else:
|
||||||
return sp.run([*pkg_manager, *args], capture_output=True, text=True)
|
return sp.run([*pkg_manager, *args], capture_output=True, text=True)
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import getpass
|
||||||
|
import time
|
||||||
|
import colorama
|
||||||
|
import subprocess as sp
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordManager:
|
||||||
|
_pw = ""
|
||||||
|
_wrong_cnt = 0
|
||||||
|
_valid = False
|
||||||
|
|
||||||
|
def get(self, msg: str = ""):
|
||||||
|
"""Get the user's password (uses cached password if PW is valid)
|
||||||
|
Otherwise prompts user
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg: The message to use for the password prompt
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The user's password
|
||||||
|
"""
|
||||||
|
if self._valid or self.validate():
|
||||||
|
return self._pw
|
||||||
|
|
||||||
|
if msg != "":
|
||||||
|
self._pw = getpass.getpass(msg)
|
||||||
|
else:
|
||||||
|
self._pw = getpass.getpass()
|
||||||
|
if self._pw != "":
|
||||||
|
if not self.validate():
|
||||||
|
print(
|
||||||
|
colorama.Fore.RED + "Error:",
|
||||||
|
colorama.Style.RESET_ALL + "Invalid Password. Please try again",
|
||||||
|
)
|
||||||
|
return self.get(msg)
|
||||||
|
return self._pw
|
||||||
|
else:
|
||||||
|
time.sleep(1)
|
||||||
|
print(
|
||||||
|
colorama.Fore.RED + "Error:",
|
||||||
|
colorama.Style.RESET_ALL + "Password cannot be empty",
|
||||||
|
)
|
||||||
|
return self.get(msg)
|
||||||
|
|
||||||
|
def validate(self) -> bool:
|
||||||
|
"""Validate that the password is correct by running sudo command
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if password is valid, False otherwise
|
||||||
|
"""
|
||||||
|
|
||||||
|
def helper():
|
||||||
|
if self._pw == "":
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
sp.run(
|
||||||
|
["sudo", "-k"], capture_output=True, text=True
|
||||||
|
).check_returncode()
|
||||||
|
sp.run(
|
||||||
|
["sudo", "-S", "echo"],
|
||||||
|
capture_output=True,
|
||||||
|
input=self._pw,
|
||||||
|
text=True,
|
||||||
|
).check_returncode()
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
self._valid = helper()
|
||||||
|
return self._valid
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from typing import List
|
||||||
|
import colorama as cl
|
||||||
|
|
||||||
|
from commands.util.printing.list import print_list
|
||||||
|
|
||||||
|
|
||||||
|
def print_diff(add: List[str], remove: List[str]):
|
||||||
|
if len(add) == 0 and len(remove) == 0:
|
||||||
|
print(
|
||||||
|
cl.Fore.BLUE
|
||||||
|
+ "-->"
|
||||||
|
+ cl.Style.DIM
|
||||||
|
+ cl.Fore.GREEN
|
||||||
|
+ " No packages changes"
|
||||||
|
+ cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
|
# Packages to be installed
|
||||||
|
if len(add) == 0:
|
||||||
|
print(
|
||||||
|
cl.Fore.BLUE
|
||||||
|
+ "-->"
|
||||||
|
+ cl.Style.DIM
|
||||||
|
+ cl.Fore.GREEN
|
||||||
|
+ " No packages to be installed"
|
||||||
|
+ cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
cl.Fore.GREEN + "==>",
|
||||||
|
cl.Style.RESET_ALL + "Packages that will be",
|
||||||
|
cl.Fore.GREEN + "installed" + cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
|
print_list(add)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Packages to be removed
|
||||||
|
if len(remove) == 0:
|
||||||
|
print(
|
||||||
|
cl.Fore.BLUE
|
||||||
|
+ "-->"
|
||||||
|
+ cl.Style.DIM
|
||||||
|
+ cl.Fore.GREEN
|
||||||
|
+ " No packages to be uninstalled"
|
||||||
|
+ cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
cl.Fore.GREEN + "==>",
|
||||||
|
cl.Style.RESET_ALL + "Packages that will be",
|
||||||
|
cl.Fore.RED + "uninstalled" + cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
|
print_list(remove)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Ask user to confirm
|
||||||
|
print(
|
||||||
|
cl.Fore.GREEN + "==>",
|
||||||
|
cl.Style.RESET_ALL
|
||||||
|
+ f"Transaction (packages): {cl.Fore.BLUE + str(len(add)) + cl.Style.RESET_ALL}",
|
||||||
|
cl.Fore.GREEN + "installed",
|
||||||
|
cl.Style.RESET_ALL
|
||||||
|
+ f"and {cl.Fore.BLUE + str(len(remove)) + cl.Style.RESET_ALL}",
|
||||||
|
cl.Fore.RED + "uninstalled" + cl.Style.RESET_ALL,
|
||||||
|
)
|
||||||
@@ -6,3 +6,15 @@
|
|||||||
- Copy normal config files into correct directories
|
- Copy normal config files into correct directories
|
||||||
- Render and copy renderables
|
- Render and copy renderables
|
||||||
- Retrieve explicitly installed packages and remove those that are not present in goal and install those that are not present in current state
|
- Retrieve explicitly installed packages and remove those that are not present in goal and install those that are not present in current state
|
||||||
|
|
||||||
|
|
||||||
|
# Ideas
|
||||||
|
- [ ] function to collect new configs
|
||||||
|
- [ ] config options for users and groups
|
||||||
|
- [ ] presets for things like desktops (like Hyprland)
|
||||||
|
- [ ] config options for the template rendering
|
||||||
|
- [ ] config options for themes
|
||||||
|
- [ ] grub config
|
||||||
|
- [ ] Dynamic selection of more configs (i.e. require syntax)
|
||||||
|
- [ ] Own config syntax?
|
||||||
|
- [ ] Autocompletion
|
||||||
|
|||||||
Reference in New Issue
Block a user