mirror of
https://github.com/kiviktnm/decman.git
synced 2026-09-19 12:08:28 +00:00
Run flatpak commands on users with UID>=1000
This commit is contained in:
@@ -1,16 +1,16 @@
|
|||||||
# Decman
|
# Decman
|
||||||
|
|
||||||
> 🎉 Support for Flatpaks was just added! 🎉
|
> 🎉 Early support for Flatpaks was just added! 🎉
|
||||||
> By default flatpak management this feature is disabled. Take a look at the example to learn how to use flatpaks. You can install to the user as well as the system installation.
|
> By default flatpak management is disabled. Support is in early stages so expect bugs.
|
||||||
|
|
||||||
> 🎉 Decman now has a AUR package! 🎉
|
|
||||||
>
|
|
||||||
> To start using the AUR package simply add `decman` to `decman.aur_packages`. To ensure a smooth change, add decman-git to ignored_packages during the conversion.
|
|
||||||
>
|
|
||||||
> ```py
|
> ```py
|
||||||
> import decman
|
> import decman
|
||||||
> decman.aur_packages += ["decman"]
|
> import decman.config
|
||||||
> decman.ignored_packages += ["decman-git"]
|
> decman.config.enable_flatpak = True
|
||||||
|
> # You can add system wide packages as well as user packages
|
||||||
|
> decman.flatpak_packages += ["org.signal.Signal"]
|
||||||
|
> decman.flatpak_user_packages["user"] = decman.flatpak_user_packages.get("user", [])
|
||||||
|
> decman.flatpak_user_packages["user"].append("dev.zed.Zed")
|
||||||
> ```
|
> ```
|
||||||
|
|
||||||
Decman is a declarative package & configuration manager for Arch Linux. It allows you to manage installed packages, your dotfiles, enabled systemd units, and run commands automatically. Your system is configured using python so your configuration can be very adaptive.
|
Decman is a declarative package & configuration manager for Arch Linux. It allows you to manage installed packages, your dotfiles, enabled systemd units, and run commands automatically. Your system is configured using python so your configuration can be very adaptive.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# from import is ok for importing classes and functions
|
# from import is ok for importing classes and functions
|
||||||
# just remember to not import variables this way
|
# just remember to not import variables this way
|
||||||
from typing import override
|
|
||||||
from decman import Module, File, Directory, UserPackage, sh, prg
|
from decman import Directory, File, Module, UserPackage, prg, sh
|
||||||
|
|
||||||
|
|
||||||
class MyModule(Module):
|
class MyModule(Module):
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
# This example covers all decman features and many useful ways of configuring a system.
|
# This example covers all decman features and many useful ways of configuring a system.
|
||||||
# Configuration can be as simple or as complex as is needed.
|
# Configuration can be as simple or as complex as is needed.
|
||||||
|
|
||||||
import socket
|
|
||||||
import os
|
import os
|
||||||
|
import socket
|
||||||
|
|
||||||
# Note: Do NOT use from imports for global variables
|
# Note: Do NOT use from imports for global variables
|
||||||
# BAD: from decman import packages/modules/etc
|
# BAD: from decman import packages/modules/etc
|
||||||
@@ -10,7 +10,7 @@ import decman
|
|||||||
import decman.config
|
import decman.config
|
||||||
|
|
||||||
# This is fine since the thing being imported is a class and not a global variable.
|
# This is fine since the thing being imported is a class and not a global variable.
|
||||||
from decman import UserPackage, File, Directory, UserRaisedError
|
from decman import Directory, File, UserPackage, UserRaisedError
|
||||||
|
|
||||||
# Flatpaks are disabled by default. This way no already installed flatpaks will get suddenly deleted.
|
# Flatpaks are disabled by default. This way no already installed flatpaks will get suddenly deleted.
|
||||||
decman.config.enable_flatpak = True
|
decman.config.enable_flatpak = True
|
||||||
|
|||||||
+37
-32
@@ -5,11 +5,10 @@ Module containing the CLI Application.
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
|
import pwd
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import pwd
|
|
||||||
|
|
||||||
import decman
|
import decman
|
||||||
import decman.config as conf
|
import decman.config as conf
|
||||||
@@ -291,7 +290,7 @@ class Core:
|
|||||||
|
|
||||||
l.print_list("Removing pacman packages:", to_remove)
|
l.print_list("Removing pacman packages:", to_remove)
|
||||||
|
|
||||||
if conf.enable_flatpak:
|
if conf.enable_flatpak and self.update_flatpaks:
|
||||||
l.print_list("Removing flatpak packages:", to_remove_flatpak)
|
l.print_list("Removing flatpak packages:", to_remove_flatpak)
|
||||||
self._remove_user_flatpaks(only_print=True)
|
self._remove_user_flatpaks(only_print=True)
|
||||||
|
|
||||||
@@ -306,20 +305,15 @@ class Core:
|
|||||||
self._remove_user_flatpaks()
|
self._remove_user_flatpaks()
|
||||||
|
|
||||||
def _remove_user_flatpaks(self, only_print: bool = False):
|
def _remove_user_flatpaks(self, only_print: bool = False):
|
||||||
"""
|
# Get all non system users (users that have uid >= 1000), also ignore nobody
|
||||||
# get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages
|
users = [
|
||||||
users = (
|
u.pw_name
|
||||||
subprocess.run(["users"], check=True, stdout=subprocess.PIPE)
|
for u in pwd.getpwall()
|
||||||
.stdout.decode()
|
if u.pw_uid >= 1000 and u.pw_name not in ("nobody",)
|
||||||
.strip()
|
]
|
||||||
.split("\n")
|
# Add root to users
|
||||||
)
|
users.append("root")
|
||||||
print()
|
|
||||||
users.append("root")"""
|
|
||||||
|
|
||||||
users = pwd.getpwall()
|
|
||||||
for user in users:
|
for user in users:
|
||||||
user = user.pw_name
|
|
||||||
currently_installed_flatpak = self.flatpak.get_installed(
|
currently_installed_flatpak = self.flatpak.get_installed(
|
||||||
as_user=True, which_user=user
|
as_user=True, which_user=user
|
||||||
)
|
)
|
||||||
@@ -353,7 +347,18 @@ class Core:
|
|||||||
|
|
||||||
# flatpak
|
# flatpak
|
||||||
if conf.enable_flatpak and self.update_flatpaks:
|
if conf.enable_flatpak and self.update_flatpaks:
|
||||||
|
l.print_summary("Upgrading flatpak packages.")
|
||||||
self.flatpak.upgrade()
|
self.flatpak.upgrade()
|
||||||
|
users = [
|
||||||
|
u.pw_name
|
||||||
|
for u in pwd.getpwall()
|
||||||
|
if u.pw_uid >= 1000 and u.pw_name not in ("nobody",)
|
||||||
|
]
|
||||||
|
# Add root to users
|
||||||
|
users.append("root")
|
||||||
|
for user in users:
|
||||||
|
l.print_summary(f"Upgrading flatpak packages for {user}.")
|
||||||
|
self.flatpak.upgrade(True, user)
|
||||||
|
|
||||||
def _install_pkgs(self):
|
def _install_pkgs(self):
|
||||||
"""
|
"""
|
||||||
@@ -373,13 +378,15 @@ class Core:
|
|||||||
|
|
||||||
l.print_list("Installing pacman packages:", to_install_pacman)
|
l.print_list("Installing pacman packages:", to_install_pacman)
|
||||||
|
|
||||||
if conf.enable_flatpak:
|
|
||||||
l.print_list("Installing flatpak packages:", to_install_flatpak)
|
|
||||||
self._install_user_flatpaks(only_print=True)
|
|
||||||
|
|
||||||
# fpm prints a summary so no need to print it twice
|
# fpm prints a summary so no need to print it twice
|
||||||
if self.only_print:
|
if self.only_print:
|
||||||
l.print_list("Installing foreign packages:", to_install_fpm)
|
l.print_list("Installing foreign packages:", to_install_fpm)
|
||||||
|
|
||||||
|
if conf.enable_flatpak and self.update_flatpaks:
|
||||||
|
l.print_list("Installing flatpak packages:", to_install_flatpak)
|
||||||
|
|
||||||
|
if self.only_print:
|
||||||
|
self._install_user_flatpaks(only_print=True)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.pacman.install(to_install_pacman)
|
self.pacman.install(to_install_pacman)
|
||||||
@@ -388,22 +395,20 @@ class Core:
|
|||||||
|
|
||||||
if conf.enable_flatpak and self.update_flatpaks:
|
if conf.enable_flatpak and self.update_flatpaks:
|
||||||
self.flatpak.install(to_install_flatpak)
|
self.flatpak.install(to_install_flatpak)
|
||||||
|
# Print summary before the action
|
||||||
|
self._install_user_flatpaks(only_print=True)
|
||||||
self._install_user_flatpaks()
|
self._install_user_flatpaks()
|
||||||
|
|
||||||
def _install_user_flatpaks(self, only_print: bool = False):
|
def _install_user_flatpaks(self, only_print: bool = False):
|
||||||
"""# get all users through a command instead of pwd because pwd also lists all 'virtual' users. add root since they can also have user installed packages
|
# Get all non system users (users that have uid >= 1000), also ignore nobody
|
||||||
users = (
|
users = [
|
||||||
subprocess.run(["users"], check=True, stdout=subprocess.PIPE)
|
u.pw_name
|
||||||
.stdout.decode()
|
for u in pwd.getpwall()
|
||||||
.strip()
|
if u.pw_uid >= 1000 and u.pw_name not in ("nobody",)
|
||||||
.split("\n")
|
]
|
||||||
)
|
# Add root to users
|
||||||
users.append("root")"""
|
users.append("root")
|
||||||
|
|
||||||
users = pwd.getpwall()
|
|
||||||
|
|
||||||
for user in users:
|
for user in users:
|
||||||
user = user.pw_name
|
|
||||||
currently_installed_flatpak = self.flatpak.get_installed(
|
currently_installed_flatpak = self.flatpak.get_installed(
|
||||||
as_user=True, which_user=user
|
as_user=True, which_user=user
|
||||||
)
|
)
|
||||||
|
|||||||
+15
-2
@@ -102,7 +102,13 @@ class Commands:
|
|||||||
"""
|
"""
|
||||||
Updates all installed flatpak REFs including runtimes and dependencies.
|
Updates all installed flatpak REFs including runtimes and dependencies.
|
||||||
"""
|
"""
|
||||||
return ["flatpak", "update", "-y", "--user" if as_user else "--system"]
|
return [
|
||||||
|
"flatpak",
|
||||||
|
"update",
|
||||||
|
"--noninteractive",
|
||||||
|
"-y",
|
||||||
|
"--user" if as_user else "--system",
|
||||||
|
]
|
||||||
|
|
||||||
def remove(self, pkgs: list[str]) -> list[str]:
|
def remove(self, pkgs: list[str]) -> list[str]:
|
||||||
"""
|
"""
|
||||||
@@ -115,7 +121,13 @@ class Commands:
|
|||||||
"""
|
"""
|
||||||
Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run.
|
Running this command will remove the listed REFs. Unused dependencies might be kept, but to remove them another command needs to be run.
|
||||||
"""
|
"""
|
||||||
return ["flatpak", "remove", "-y", "--user" if as_user else "--system"] + pkgs
|
return [
|
||||||
|
"flatpak",
|
||||||
|
"remove",
|
||||||
|
"--noninteractive",
|
||||||
|
"-y",
|
||||||
|
"--user" if as_user else "--system",
|
||||||
|
] + pkgs
|
||||||
|
|
||||||
def remove_unused_flatpak(self, as_user: bool = False) -> list[str]:
|
def remove_unused_flatpak(self, as_user: bool = False) -> list[str]:
|
||||||
"""
|
"""
|
||||||
@@ -124,6 +136,7 @@ class Commands:
|
|||||||
return [
|
return [
|
||||||
"flatpak",
|
"flatpak",
|
||||||
"remove",
|
"remove",
|
||||||
|
"--noninteractive",
|
||||||
"-y",
|
"-y",
|
||||||
"--unused",
|
"--unused",
|
||||||
"--user" if as_user else "--system",
|
"--user" if as_user else "--system",
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ Library module for decman.
|
|||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
import pty
|
import pty
|
||||||
|
import pwd
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import typing
|
import typing
|
||||||
import pwd
|
|
||||||
|
|
||||||
import decman
|
import decman
|
||||||
import decman.config as conf
|
import decman.config as conf
|
||||||
@@ -1035,7 +1035,9 @@ class Flatpak:
|
|||||||
user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}."
|
user_facing_msg=f"Failed to get installed flatpak packages using '{error.cmd}'. Output: {error.stdout}."
|
||||||
) from error
|
) from error
|
||||||
|
|
||||||
def install(self, packages: list[str], as_user: bool = False, which_user: str = ""):
|
def install(
|
||||||
|
self, packages: list[str], as_user: bool = False, which_user: str = "root"
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Install the listed flatpak packages.
|
Install the listed flatpak packages.
|
||||||
"""
|
"""
|
||||||
@@ -1053,7 +1055,6 @@ class Flatpak:
|
|||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
conf.commands.install_flatpak_pkgs(packages, as_user),
|
conf.commands.install_flatpak_pkgs(packages, as_user),
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
user=uinfo[0],
|
user=uinfo[0],
|
||||||
group=uinfo[1],
|
group=uinfo[1],
|
||||||
env=user_env if as_user else env,
|
env=user_env if as_user else env,
|
||||||
@@ -1064,19 +1065,33 @@ class Flatpak:
|
|||||||
f"Failed to install flatpak packages. Process exited with code {proc.returncode}."
|
f"Failed to install flatpak packages. Process exited with code {proc.returncode}."
|
||||||
)
|
)
|
||||||
|
|
||||||
def upgrade(self) -> None:
|
def upgrade(self, as_user: bool = False, which_user: str = "root") -> None:
|
||||||
"""
|
"""
|
||||||
Upgrade all flatpak packages.
|
Upgrade all flatpak packages.
|
||||||
"""
|
"""
|
||||||
|
uinfo: tuple[int, int] = (0, 0)
|
||||||
|
if as_user:
|
||||||
|
uinfo = get_user_info(which_user)
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
user_env = env.copy()
|
||||||
|
user_env["HOME"] = os.path.expanduser(f"~{which_user}")
|
||||||
|
|
||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
conf.commands.upgrade_flatpak(), check=True, stdout=subprocess.PIPE
|
conf.commands.upgrade_flatpak(as_user=True),
|
||||||
|
check=True,
|
||||||
|
user=uinfo[0],
|
||||||
|
group=uinfo[1],
|
||||||
|
env=user_env if as_user else env,
|
||||||
)
|
)
|
||||||
if not proc.returncode == 0:
|
if not proc.returncode == 0:
|
||||||
raise err.UserFacingError(
|
raise err.UserFacingError(
|
||||||
f"Failed to upgrade flatpak packages. Process exited with code {proc.returncode}."
|
f"Failed to upgrade flatpak packages. Process exited with code {proc.returncode}."
|
||||||
)
|
)
|
||||||
|
|
||||||
def remove(self, packages: list[str], as_user: bool = False, which_user: str = ""):
|
def remove(
|
||||||
|
self, packages: list[str], as_user: bool = False, which_user: str = "root"
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Remove all the listed packages and their unused dependecies. This has to happen in two steps.
|
Remove all the listed packages and their unused dependecies. This has to happen in two steps.
|
||||||
"""
|
"""
|
||||||
@@ -1094,7 +1109,6 @@ class Flatpak:
|
|||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
conf.commands.remove_flatpak(packages, as_user),
|
conf.commands.remove_flatpak(packages, as_user),
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
user=uinfo[0],
|
user=uinfo[0],
|
||||||
group=uinfo[1],
|
group=uinfo[1],
|
||||||
env=user_env if as_user else env,
|
env=user_env if as_user else env,
|
||||||
@@ -1108,7 +1122,6 @@ class Flatpak:
|
|||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
conf.commands.remove_unused_flatpak(as_user),
|
conf.commands.remove_unused_flatpak(as_user),
|
||||||
check=True,
|
check=True,
|
||||||
stdout=subprocess.PIPE,
|
|
||||||
user=uinfo[0] if as_user else 0,
|
user=uinfo[0] if as_user else 0,
|
||||||
group=uinfo[1] if as_user else 0,
|
group=uinfo[1] if as_user else 0,
|
||||||
env=user_env if as_user else env,
|
env=user_env if as_user else env,
|
||||||
|
|||||||
Reference in New Issue
Block a user