mirror of
https://github.com/kiviktnm/decman.git
synced 2026-09-19 20:18:28 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f3910a6bc9 | ||
|
|
bce9be5ebc | ||
|
|
ec19b21244 | ||
|
|
47ea816d3f |
@@ -1,5 +1,15 @@
|
||||
# Decman
|
||||
|
||||
> 🎉 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"]
|
||||
> ```
|
||||
|
||||
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.
|
||||
|
||||
If you want, you can also use decman with other configuration languages. See the [example with TOML later in this README](#decman-with-other-configuration-languages).
|
||||
@@ -111,17 +121,22 @@ decman --help
|
||||
Clone the decman PKGBUILD:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/kiviktnm/decman-pkgbuild.git
|
||||
git clone https://aur.archlinux.org/decman.git
|
||||
```
|
||||
|
||||
Review the PKGBUILD and install it.
|
||||
|
||||
```sh
|
||||
cd decman-pkgbuild
|
||||
cd decman
|
||||
makepkg -si
|
||||
```
|
||||
|
||||
So far I have not created an AUR package for decman, because I'm not sure if other people would find decman useful.
|
||||
Remember to add decman to its own configuration.
|
||||
|
||||
```py
|
||||
import decman
|
||||
decman.aur_packages += ["decman"]
|
||||
```
|
||||
|
||||
## What decman manages?
|
||||
|
||||
@@ -129,6 +144,8 @@ So far I have not created an AUR package for decman, because I'm not sure if oth
|
||||
|
||||
Decman can be used to install pacman packages. Decman will install all packages defined in the source and **remove** all explicitly installed packages not defined in the source. You don't need to list dependencies as those will be handeled by pacman. You can set packages to be ignored by decman, so that it won't install them nor remove them.
|
||||
|
||||
Please keep in mind that decman doesn't play well with package groups, since all packages part of that group will be installed explicitly. After the initial run decman will now try to remove those packages since it only knows that the group itself should be explicitly installed. Instead of package groups, use meta packages.
|
||||
|
||||
```py
|
||||
# Include both foreign and pacman packages here.
|
||||
decman.ignored_packages += ["yay", "opendoas"]
|
||||
@@ -145,12 +162,13 @@ Decman can install AUR packages as well as user defined packages. Foreign packag
|
||||
Here is an example of a user package. Managing user packages is somewhat cumbersome as you have to declare their versions, dependencies and make dependencies manually. However, you probably won't install many user packages anyway.
|
||||
|
||||
```py
|
||||
# Note, decman now has a aur package, I recommend using that instead.
|
||||
# Also, this example may be out of date
|
||||
decman.user_packages.append(
|
||||
decman.UserPackage(
|
||||
pkgname="decman-git",
|
||||
# Note, this example may not be up to date
|
||||
provides=["decman"],
|
||||
version="0.3.3",
|
||||
version="0.3.4",
|
||||
dependencies=["python", "python-requests", "devtools", "pacman", "systemd", "git"],
|
||||
make_dependencies=[
|
||||
"python-setuptools", "python-build", "python-installer", "python-wheel"
|
||||
@@ -278,10 +296,11 @@ aur_packages = ["protonvpn"]
|
||||
enabled_systemd_units = ["NetworkManager.service"]
|
||||
ignored_packages = ["yay"]
|
||||
user_packages = [{
|
||||
# Note, decman now has a aur package, I recommend using that instead.
|
||||
# Also, this example may be out of date
|
||||
pkgname="decman-git",
|
||||
# Note, this example may not be up to date
|
||||
provides=["decman"],
|
||||
version="0.3.3",
|
||||
version="0.3.4",
|
||||
dependencies=["python", "python-requests", "devtools", "pacman", "systemd", "git"],
|
||||
make_dependencies=[
|
||||
"python-setuptools", "python-build", "python-installer", "python-wheel"
|
||||
|
||||
+14
-8
@@ -4,7 +4,6 @@ from decman import Module, File, Directory, UserPackage, sh, prg
|
||||
|
||||
|
||||
class MyModule(Module):
|
||||
|
||||
def __init__(self):
|
||||
self.pkgs = ["rust"]
|
||||
self.update_rustup = False
|
||||
@@ -28,6 +27,8 @@ class MyModule(Module):
|
||||
# or run a program with arguments.
|
||||
prg(["usermod", "--append", "--groups", "mygroup", "kk"])
|
||||
|
||||
# NOTE! Removing a enabled module from decman.module means that on_disable will not run.
|
||||
# Instead disable the module.
|
||||
def on_disable(self):
|
||||
# You can run commands as any user
|
||||
sh("whoami", user="kk")
|
||||
@@ -58,18 +59,21 @@ class MyModule(Module):
|
||||
def files(self) -> dict[str, File]:
|
||||
# Variables are substituted in text files automatically.
|
||||
return {
|
||||
"/usr/local/bin/say-hello":
|
||||
File(content="#!/usr/bin/env bash\necho %msg%", permissions=0o755),
|
||||
"/usr/local/bin/say-hello": File(
|
||||
content="#!/usr/bin/env bash\necho %msg%", permissions=0o755
|
||||
),
|
||||
# Variables are not substituted in binary files.
|
||||
"/usr/local/share/say-hello/image.png":
|
||||
File(source_file="files/i-dont-exist.png", bin_file=True),
|
||||
"/usr/local/share/say-hello/image.png": File(
|
||||
source_file="files/i-dont-exist.png", bin_file=True
|
||||
),
|
||||
}
|
||||
|
||||
def directories(self) -> dict[str, Directory]:
|
||||
# Directories are handeled the same way. Variables are substituted in text files.
|
||||
return {
|
||||
"/home/kk/.config/mod-app/":
|
||||
Directory(source_directory="files/app-config", owner="kk")
|
||||
"/home/kk/.config/mod-app/": Directory(
|
||||
source_directory="files/app-config", owner="kk"
|
||||
)
|
||||
}
|
||||
|
||||
# Packages and systemd units are basically the same with modules as without modules.
|
||||
@@ -79,10 +83,12 @@ class MyModule(Module):
|
||||
return self.pkgs
|
||||
|
||||
def user_packages(self) -> list[UserPackage]:
|
||||
# Note, decman now has a aur package, I recommend using that instead.
|
||||
# Also, this example may be out of date
|
||||
return [
|
||||
UserPackage(
|
||||
pkgname="decman-git",
|
||||
version="0.3.3",
|
||||
version="0.3.4",
|
||||
provides=["decman"],
|
||||
dependencies=[
|
||||
"python",
|
||||
|
||||
+5
-3
@@ -26,7 +26,7 @@ decman.packages += ["python", "python", "devtools", "git", "networkmanager"]
|
||||
decman.ignored_packages += ["rustup", "yay"]
|
||||
|
||||
# Installing AUR packages is easy.
|
||||
decman.aur_packages += ["protonvpn"]
|
||||
decman.aur_packages += ["decman", "protonvpn"]
|
||||
|
||||
# To import GPG keys, set the GNUPGHOME environment variable.
|
||||
# It can easily be done with python as well.
|
||||
@@ -36,11 +36,13 @@ decman.config.makepkg_user = "kk"
|
||||
|
||||
# You can also install packages from anywhere, but then you must include some
|
||||
# information about the package. The git_url is the url to the PKGBUILD,
|
||||
# This example may not be up to date, but you should keep these up to date with the PKGBUILD.
|
||||
#
|
||||
# Note, decman now has a aur package, I recommend using that instead.
|
||||
# Also, this example may be out of date
|
||||
decman.user_packages.append(
|
||||
UserPackage(
|
||||
pkgname="decman-git",
|
||||
version="0.3.3",
|
||||
version="0.3.4",
|
||||
provides=["decman"],
|
||||
dependencies=[
|
||||
"python",
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "decman"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
description = "Declarative package & configuration manager for Arch Linux."
|
||||
license = {file = "LICENSE"}
|
||||
authors = [
|
||||
|
||||
@@ -4,6 +4,7 @@ Library module for decman.
|
||||
|
||||
import json
|
||||
import os
|
||||
import pty
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
@@ -907,55 +908,18 @@ def echo_and_capture_command(program: list[str]) -> tuple[int, str]:
|
||||
|
||||
Returns a tuple containing the return code of the program as well as all output of the program.
|
||||
"""
|
||||
with subprocess.Popen(
|
||||
program, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
) as process:
|
||||
os.set_blocking(process.stdout.fileno(), False)
|
||||
|
||||
output_thread = _OutputCapturingThread(process.stdout)
|
||||
output_thread.start()
|
||||
output = ""
|
||||
|
||||
os.set_blocking(sys.stdin.fileno(), False)
|
||||
def read(fd):
|
||||
nonlocal output
|
||||
buffer = os.read(fd, 1024)
|
||||
output += buffer.decode(encoding="utf-8")
|
||||
return buffer
|
||||
|
||||
# Capture stdin and forward it to the process in a non-blocking manner
|
||||
while process.poll() is None:
|
||||
inp = sys.stdin.readline()
|
||||
returncode = os.waitstatus_to_exitcode(pty.spawn(program, read))
|
||||
|
||||
if inp:
|
||||
process.stdin.write(inp.encode())
|
||||
process.stdin.flush()
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
os.set_blocking(sys.stdin.fileno(), True)
|
||||
|
||||
output_thread.done = True
|
||||
output_thread.join()
|
||||
|
||||
# Capture any output that may not have been yet captured
|
||||
output = output_thread.output
|
||||
missing_output = process.stdout.read()
|
||||
if missing_output:
|
||||
output += missing_output.decode()
|
||||
|
||||
return (process.returncode, output)
|
||||
|
||||
|
||||
class _OutputCapturingThread(threading.Thread):
|
||||
def __init__(self, stream):
|
||||
super().__init__()
|
||||
self._stream = stream
|
||||
self.output = ""
|
||||
self.done = False
|
||||
|
||||
def run(self):
|
||||
while not self.done and not self._stream.closed:
|
||||
output = self._stream.read(1000)
|
||||
if output:
|
||||
output = output.decode()
|
||||
self.output += output
|
||||
print(output, end="", flush=True)
|
||||
time.sleep(0.1)
|
||||
return (returncode, output)
|
||||
|
||||
|
||||
class Systemd:
|
||||
|
||||
Reference in New Issue
Block a user