Run flatpak commands on users with UID>=1000

This commit is contained in:
Kivi Kaitaniemi
2025-12-10 00:29:15 +02:00
parent a54fad6570
commit 2378f49676
6 changed files with 85 additions and 54 deletions
+8 -8
View File
@@ -1,16 +1,16 @@
# Decman
> 🎉 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.
> 🎉 Early support for Flatpaks was just added! 🎉
> 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
> import decman
> decman.aur_packages += ["decman"]
> decman.ignored_packages += ["decman-git"]
> import decman.config
> 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.
+2 -2
View File
@@ -1,7 +1,7 @@
# from import is ok for importing classes and functions
# 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):
+2 -2
View File
@@ -1,8 +1,8 @@
# 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.
import socket
import os
import socket
# Note: Do NOT use from imports for global variables
# BAD: from decman import packages/modules/etc
@@ -10,7 +10,7 @@ import decman
import decman.config
# 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.
decman.config.enable_flatpak = True
+37 -32
View File
@@ -5,11 +5,10 @@ Module containing the CLI Application.
import argparse
import os
import pwd
import shutil
import subprocess
import sys
import traceback
import pwd
import decman
import decman.config as conf
@@ -291,7 +290,7 @@ class Core:
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)
self._remove_user_flatpaks(only_print=True)
@@ -306,20 +305,15 @@ class Core:
self._remove_user_flatpaks()
def _remove_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
users = (
subprocess.run(["users"], check=True, stdout=subprocess.PIPE)
.stdout.decode()
.strip()
.split("\n")
)
print()
users.append("root")"""
users = pwd.getpwall()
# Get all non system users (users that have uid >= 1000), also ignore nobody
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:
user = user.pw_name
currently_installed_flatpak = self.flatpak.get_installed(
as_user=True, which_user=user
)
@@ -353,7 +347,18 @@ class Core:
# flatpak
if conf.enable_flatpak and self.update_flatpaks:
l.print_summary("Upgrading flatpak packages.")
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):
"""
@@ -373,13 +378,15 @@ class Core:
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
if self.only_print:
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
self.pacman.install(to_install_pacman)
@@ -388,22 +395,20 @@ class Core:
if conf.enable_flatpak and self.update_flatpaks:
self.flatpak.install(to_install_flatpak)
# Print summary before the action
self._install_user_flatpaks(only_print=True)
self._install_user_flatpaks()
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
users = (
subprocess.run(["users"], check=True, stdout=subprocess.PIPE)
.stdout.decode()
.strip()
.split("\n")
)
users.append("root")"""
users = pwd.getpwall()
# Get all non system users (users that have uid >= 1000), also ignore nobody
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:
user = user.pw_name
currently_installed_flatpak = self.flatpak.get_installed(
as_user=True, which_user=user
)
+15 -2
View File
@@ -102,7 +102,13 @@ class Commands:
"""
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]:
"""
@@ -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.
"""
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]:
"""
@@ -124,6 +136,7 @@ class Commands:
return [
"flatpak",
"remove",
"--noninteractive",
"-y",
"--unused",
"--user" if as_user else "--system",
+21 -8
View File
@@ -5,11 +5,11 @@ Library module for decman.
import json
import os
import pty
import pwd
import shutil
import subprocess
import time
import typing
import pwd
import decman
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}."
) 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.
"""
@@ -1053,7 +1055,6 @@ class Flatpak:
proc = subprocess.run(
conf.commands.install_flatpak_pkgs(packages, as_user),
check=True,
stdout=subprocess.PIPE,
user=uinfo[0],
group=uinfo[1],
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}."
)
def upgrade(self) -> None:
def upgrade(self, as_user: bool = False, which_user: str = "root") -> None:
"""
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(
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:
raise err.UserFacingError(
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.
"""
@@ -1094,7 +1109,6 @@ class Flatpak:
proc = subprocess.run(
conf.commands.remove_flatpak(packages, as_user),
check=True,
stdout=subprocess.PIPE,
user=uinfo[0],
group=uinfo[1],
env=user_env if as_user else env,
@@ -1108,7 +1122,6 @@ class Flatpak:
proc = subprocess.run(
conf.commands.remove_unused_flatpak(as_user),
check=True,
stdout=subprocess.PIPE,
user=uinfo[0] if as_user else 0,
group=uinfo[1] if as_user else 0,
env=user_env if as_user else env,