Compare commits
37 Commits
6e497fdfd2
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 36d3c6f992 | |||
| 217ccadc74 | |||
| d391c53c6c | |||
| 24c52c1bba | |||
| 80d7b3d86e | |||
| 72b477381f | |||
| e8d8429bc9 | |||
| afe8d29340 | |||
| 78eecfc81a | |||
| e24eb647ca | |||
| 72a8ceb741 | |||
| ecb2952a7e | |||
| b7218c2a82 | |||
| 7b1dfe6ebc | |||
| 190fb86758 | |||
| f9e6120910 | |||
| 31426c006b | |||
| 2fd69cc595 | |||
| 541a876307 | |||
| 5126e0373f | |||
| 299ac49b40 | |||
| b6901b59e6 | |||
| bcd0339d88 | |||
| 4017e0263b | |||
| b71d18cdc9 | |||
| 043b2618a3 | |||
| c264a5bea2 | |||
| bb123c23a1 | |||
| 414c065df4 | |||
| 35c976fcac | |||
| b8e2d68469 | |||
| 5734c0d524 | |||
| f5386d0e98 | |||
| ead9e3a3f7 | |||
| 711b89a0d6 | |||
| 0697bef7d5 | |||
| 6018d6256d |
@@ -0,0 +1,28 @@
|
|||||||
|
# Maintainer: Janis Hutz <development@janishutz.com>
|
||||||
|
|
||||||
|
pkgname=archmgr-git
|
||||||
|
pkgver=0.0.0
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc='A nixos-like declarative config and package manager for Arch Linux'
|
||||||
|
arch=('any')
|
||||||
|
url="https://github.com/janishutz/archmgr"
|
||||||
|
license=('GPL3')
|
||||||
|
depends=('python', 'python-pyaml')
|
||||||
|
makedepends=('git')
|
||||||
|
provides=('archmgr')
|
||||||
|
conflicts=('archmgr')
|
||||||
|
source=("$pkgname"::git+${url}.git)
|
||||||
|
sha256sums=('SKIP') # TODO: Add?
|
||||||
|
|
||||||
|
pkgver() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
|
||||||
|
# TODO: For the non-git pkgbuild, need to use different output
|
||||||
|
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${pkgname}"
|
||||||
|
|
||||||
|
# TODO: Need to finish
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,3 +3,9 @@
|
|||||||
A nixos-like declarative config and package manager for Arch Linux (or any other distro, with some tweaks).
|
A nixos-like declarative config and package manager for Arch Linux (or any other distro, with some tweaks).
|
||||||
|
|
||||||
See the [Wiki](https://git.janishutz.com/janishutz/archmgr/wiki)
|
See the [Wiki](https://git.janishutz.com/janishutz/archmgr/wiki)
|
||||||
|
|
||||||
|
## WIP
|
||||||
|
This project is very much Work In Progress.
|
||||||
|
The configs will likely move to python-based configs from the current yaml-based ones to provide you with more flexibility.
|
||||||
|
|
||||||
|
`archmgr` will automatically create a basic setup if you run `archmgr init`, so you can get up and running quickly
|
||||||
|
|||||||
-38
@@ -1,38 +0,0 @@
|
|||||||
import cli.args as cliargs
|
|
||||||
|
|
||||||
import commands.commit as commit
|
|
||||||
import commands.config as config
|
|
||||||
import commands.init as init
|
|
||||||
import commands.pull as pull
|
|
||||||
import commands.push as push
|
|
||||||
|
|
||||||
args, ap = cliargs.add_cli_args()
|
|
||||||
|
|
||||||
if args.cmd == None:
|
|
||||||
ap.print_help()
|
|
||||||
print("\nSpecify a subcommand, '-h' or '-v'")
|
|
||||||
exit(64)
|
|
||||||
|
|
||||||
print(
|
|
||||||
"""
|
|
||||||
_
|
|
||||||
( )
|
|
||||||
_ _ _ __ ___| |__ ___ ___ __ _ __
|
|
||||||
/ _ ) __)/ ___) _ \\ _ _ \\/ _ \\ __)
|
|
||||||
( (_| | | ( (___| | | | ( ) ( ) | (_) | |
|
|
||||||
\\__ _)_) \\____)_) (_)_) (_) (_)\\__ |_)
|
|
||||||
( )_) |
|
|
||||||
\\___/
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
|
|
||||||
if args.cmd == "commit":
|
|
||||||
commit.commit(args.dry_run, args.force, args.no_commit)
|
|
||||||
elif args.cmd == "config":
|
|
||||||
config.config()
|
|
||||||
elif args.cmd == "init":
|
|
||||||
init.init(args.force)
|
|
||||||
elif args.cmd == "pull":
|
|
||||||
pull.pull(args.rebase, args.apply)
|
|
||||||
elif args.cmd == "push":
|
|
||||||
push.push(args.force)
|
|
||||||
Executable
+9
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
# PYTHON_ARGCOMPLETE_OK
|
||||||
|
|
||||||
|
# TODO: Re-export the config stuff
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
from app import run as _run
|
||||||
|
|
||||||
|
_run()
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
# The main app
|
||||||
|
from typing import cast
|
||||||
|
import cli as cliargs
|
||||||
|
|
||||||
|
import commands.commit as commit
|
||||||
|
import commands.config as config
|
||||||
|
import commands.init as init
|
||||||
|
import commands.pull as pull
|
||||||
|
import commands.push as push
|
||||||
|
import commands.prepare as setup
|
||||||
|
import commands.show as show
|
||||||
|
|
||||||
|
|
||||||
|
def run():
|
||||||
|
args, ap = cliargs.add_cli_args()
|
||||||
|
|
||||||
|
if args.cmd == None:
|
||||||
|
ap.print_help()
|
||||||
|
print("\nSpecify a subcommand, '-h' or '-v'")
|
||||||
|
exit(64)
|
||||||
|
|
||||||
|
print("""
|
||||||
|
_
|
||||||
|
( )
|
||||||
|
_ _ _ __ ___| |__ ___ ___ __ _ __
|
||||||
|
/ _ ) __)/ ___) _ \\ _ _ \\/ _ \\ __)
|
||||||
|
( (_| | | ( (___| | | | ( ) ( ) | (_) | |
|
||||||
|
\\__ _)_) \\____)_) (_)_) (_) (_)\\__ |_)
|
||||||
|
( )_) |
|
||||||
|
\\___/
|
||||||
|
""")
|
||||||
|
|
||||||
|
conf = {}
|
||||||
|
try:
|
||||||
|
if args.cmd == "commit":
|
||||||
|
commit.commit(conf, args.force, args.no_render)
|
||||||
|
elif args.cmd == "config":
|
||||||
|
config.config(args, conf)
|
||||||
|
elif args.cmd == "init":
|
||||||
|
init.init(args.force)
|
||||||
|
elif args.cmd == "setup":
|
||||||
|
setup.setup()
|
||||||
|
elif args.cmd == "pull":
|
||||||
|
pull.pull(conf, args.rebase, args.apply)
|
||||||
|
elif args.cmd == "push":
|
||||||
|
push.push(args.force)
|
||||||
|
elif args.cmd == "show":
|
||||||
|
show.show(conf, cast(ArchMgrTemplates, {}), args)
|
||||||
|
except KeyboardInterrupt as e:
|
||||||
|
exit(130)
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# CLI argument parsing and setup for it
|
||||||
|
import argparse
|
||||||
|
import argcomplete
|
||||||
|
|
||||||
|
from args import commit, init, pull, push, show
|
||||||
|
|
||||||
|
|
||||||
|
def add_cli_args():
|
||||||
|
ap = argparse.ArgumentParser(
|
||||||
|
"archmgr",
|
||||||
|
description="A nixos-like declarative config and package manager for Arch Linux (or any other distro, with some tweaks)",
|
||||||
|
usage="archmgr [command] [options]",
|
||||||
|
)
|
||||||
|
ap.add_argument("-v", "--version", action="version", version="%(prog)s V0.0.1")
|
||||||
|
sp = ap.add_subparsers(
|
||||||
|
title="commands",
|
||||||
|
metavar="Use 'archmgr [command] --help' to see help for each command",
|
||||||
|
dest="cmd",
|
||||||
|
)
|
||||||
|
|
||||||
|
# ┌ ┐
|
||||||
|
# │ Subcommands │
|
||||||
|
# └ ┘
|
||||||
|
sp.add_parser("setup", help="Do initial setup, like installing required tools")
|
||||||
|
commit.add_parser(sp)
|
||||||
|
sp.add_parser("config", help="prints information about your config")
|
||||||
|
init.add_parser(sp)
|
||||||
|
pull.add_parser(sp)
|
||||||
|
push.add_parser(sp)
|
||||||
|
show.add_parser(sp)
|
||||||
|
|
||||||
|
argcomplete.autocomplete(ap)
|
||||||
|
|
||||||
|
return ap.parse_args(), ap
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import argparse as ap
|
||||||
|
|
||||||
|
|
||||||
|
def add_parser(sp: ap._SubParsersAction[ap.ArgumentParser]):
|
||||||
|
commit = sp.add_parser(
|
||||||
|
"commit", help="apply pending changes and commit to git repo"
|
||||||
|
)
|
||||||
|
commit.add_argument(
|
||||||
|
"-f", "--force", help="force apply (skips prompts)", action="store_true"
|
||||||
|
)
|
||||||
|
commit.add_argument(
|
||||||
|
"--no-render", "-r", help="do not re-render renderables", action="store_true"
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import argparse as ap
|
||||||
|
|
||||||
|
|
||||||
|
def add_parser(sp: ap._SubParsersAction[ap.ArgumentParser]):
|
||||||
|
init = sp.add_parser("init", help="initialize a new archmgr repository")
|
||||||
|
init.add_argument(
|
||||||
|
"--force",
|
||||||
|
"-f",
|
||||||
|
help="resets the git repository and initializes new archmgr repo",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import argparse as ap
|
||||||
|
|
||||||
|
|
||||||
|
def add_parser(sp: ap._SubParsersAction[ap.ArgumentParser]):
|
||||||
|
pull = sp.add_parser("pull", help="pull changes from git remote")
|
||||||
|
pull.add_argument("--rebase", "-r", help="execute rebase", action="store_true")
|
||||||
|
pull.add_argument(
|
||||||
|
"--apply",
|
||||||
|
"-a",
|
||||||
|
help="also apply the changes to the local system",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import argparse as ap
|
||||||
|
|
||||||
|
|
||||||
|
def add_parser(sp: ap._SubParsersAction[ap.ArgumentParser]):
|
||||||
|
push = sp.add_parser("push", help="push pending changes to git remote")
|
||||||
|
push.add_argument(
|
||||||
|
"--force",
|
||||||
|
"-f",
|
||||||
|
help="force push (overriding possible remote changes)",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import argparse as ap
|
||||||
|
|
||||||
|
|
||||||
|
def add_parser(sp: ap._SubParsersAction[ap.ArgumentParser]):
|
||||||
|
show = sp.add_parser("show", help="get information about configuration presets")
|
||||||
|
show_sp = show.add_subparsers(
|
||||||
|
title="config presets to show details on",
|
||||||
|
dest="show",
|
||||||
|
required=True,
|
||||||
|
)
|
||||||
|
show_sp.add_parser("config", help="show details about your configuration. Alias of config show")
|
||||||
|
pkgs = show_sp.add_parser("pkgs", help="show details on package presets")
|
||||||
|
pkgs.add_argument("show_pkg")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# Declare a file explicitly if it has different permissions
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def clone_git_repo(repo_url: str, clone_path: str, branch: str = "DEFAULT"):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
def add_packages(pkgs: list[str]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def add_package_bundles(pkgs: list[str]):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def enable_reflector():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def enable_repo(name: str):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
def add_template_data(name: str, data: str):
|
||||||
|
"""Replace all occurrences of variable specified by `name` by
|
||||||
|
the specified data
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The variable to replace
|
||||||
|
data: The data to replace it with
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def add_template_array(name: str, template: str, data: list[str]):
|
||||||
|
"""Replace all occurrences of variable specified by `name` in the files with
|
||||||
|
the specified template, where {{ data }} is replaced with the each element
|
||||||
|
of the data argument
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the variable to replace
|
||||||
|
template:
|
||||||
|
Template syntax, with {{ data }} as variable, see example below or docs.
|
||||||
|
Recursive replacement is supported up to one layer deep
|
||||||
|
data: A list of data that is substituted into the {{ data }} variable in the template
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```python
|
||||||
|
add_template_array("test", "- Hello World {{ data }}\\n", [0, 1])
|
||||||
|
```
|
||||||
|
for example replaces {{ test }} in a file with
|
||||||
|
```
|
||||||
|
- Hello World 0
|
||||||
|
|
||||||
|
- Hello World 1
|
||||||
|
```
|
||||||
|
"""
|
||||||
|
# TODO: Recursive replacement
|
||||||
|
pass
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from typing import Literal
|
||||||
|
|
||||||
|
|
||||||
|
def add_custom_theme():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def custom_theme_color_source(src: Literal["wallpaper"] | Literal["default"]):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: For desktop environments, what command??
|
||||||
|
def set_wallpaper(path: str):
|
||||||
|
pass
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
def set_gtk_theme():
|
||||||
|
pass
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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
|
||||||
|
"""
|
||||||
|
removed = []
|
||||||
|
diffed_out = []
|
||||||
|
for pkg in actual:
|
||||||
|
try:
|
||||||
|
diffed_out.append(target.index(pkg))
|
||||||
|
except Exception:
|
||||||
|
removed.append(pkg)
|
||||||
|
|
||||||
|
diffed_out.reverse()
|
||||||
|
new_pkgs = []
|
||||||
|
if len(diffed_out) > 0:
|
||||||
|
curr = diffed_out.pop()
|
||||||
|
for i, pkg in enumerate(target):
|
||||||
|
if i != curr:
|
||||||
|
new_pkgs.append(pkg)
|
||||||
|
else:
|
||||||
|
if len(diffed_out) > 0:
|
||||||
|
curr = diffed_out.pop()
|
||||||
|
|
||||||
|
return (new_pkgs, removed)
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def init(dirname: str) -> bool:
|
||||||
|
"""Initialize a new git repo in the directory
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dirname: The directory to create it in
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
return subprocess.call("git init", cwd=dirname) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def commit(msg: str):
|
||||||
|
"""Create a new commit
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg: The commit message
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call('git commit -am "' + msg + '"', cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def branch_add(name: str):
|
||||||
|
"""Create a new git branch
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the branch to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git branch " + name, cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def branch_switch(name: str):
|
||||||
|
"""Switch to a git branch
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the branch to switch to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git checkout " + name, cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def branch_show():
|
||||||
|
"""Create a new git branch
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the branch to create
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git branch", cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def branch_delete(name: str):
|
||||||
|
"""Delete a git branch
|
||||||
|
|
||||||
|
Args:
|
||||||
|
name: The name of the branch to delete
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git branch -d " + name, cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def pull(rebase: bool = False):
|
||||||
|
"""Pull git changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
rebase: Whether to rebase or not
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git pull" + (" --rebase" if rebase else ""), cwd=dir) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def push(force: bool = False):
|
||||||
|
"""Push git changes
|
||||||
|
|
||||||
|
Args:
|
||||||
|
force: Whether to force push
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True on success, False on error
|
||||||
|
"""
|
||||||
|
dir = os.getcwd()
|
||||||
|
return subprocess.call("git push" + (" --force" if force else ""), cwd=dir) == 0
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
|
from password_mgr import PasswordManager
|
||||||
|
|
||||||
|
|
||||||
def choice(default: str, options: str, msg: str) -> str:
|
def choice(default: str, options: str, msg: str) -> str:
|
||||||
default = default.lower()
|
default = default.lower()
|
||||||
@@ -28,3 +30,15 @@ def confirm_overwrite(msg: Optional[str] = ""):
|
|||||||
return confirm(
|
return confirm(
|
||||||
False, (msg if msg != "" else "Do you really want to overwrite your changes?")
|
False, (msg if msg != "" else "Do you really want to overwrite your changes?")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Use class to store the password
|
||||||
|
pw_manager = PasswordManager()
|
||||||
|
|
||||||
|
|
||||||
|
def password():
|
||||||
|
return pw_manager.get()
|
||||||
|
|
||||||
|
|
||||||
|
def unlock_sudo():
|
||||||
|
return pw_manager.validate()
|
||||||
@@ -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 @@
|
|||||||
|
import subprocess as sp
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
from util.input import password
|
||||||
|
|
||||||
|
pkg_manager = ["yay", "--noconfirm"]
|
||||||
|
|
||||||
|
|
||||||
|
def list_explicitly_installed() -> List[str]:
|
||||||
|
"""List all packages explicitly installed
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The package list
|
||||||
|
"""
|
||||||
|
return run_pkg_manager_cmd(["-Qeq"]).stdout.split()
|
||||||
|
|
||||||
|
|
||||||
|
def remove_orphans() -> bool:
|
||||||
|
"""Removes all orphan packages
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if successful, False otherwise
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
sp.run(
|
||||||
|
["pacman", "-Qdtq", "|", "sudo", "pacman", "-Rns", "--noconfirm", "-"],
|
||||||
|
capture_output=True,
|
||||||
|
).returncode
|
||||||
|
== 0
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def uninstall_package_list(pkgs: List[str]) -> bool:
|
||||||
|
"""Uninstall all packages in the list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pkgs: A list of packages to uninstall
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
True if successful, False otherwise
|
||||||
|
"""
|
||||||
|
# TODO: Add guard to protect against uninstalling archmgr and confirm uninstalling crucial pkgs
|
||||||
|
# pkgs.index("archmgr")
|
||||||
|
# TODO: Consider if we want to print out stdout and stderr on err
|
||||||
|
return run_pkg_manager_cmd(["-Rs", *pkgs], True).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def install_package_list(pkgs: List[str]) -> bool:
|
||||||
|
"""Install all packages in the list
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pkgs: A list of packages to install
|
||||||
|
"""
|
||||||
|
return run_pkg_manager_cmd(["-S", *pkgs], True).returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def run_pkg_manager_cmd(
|
||||||
|
args: List[str], use_sudo: bool = False
|
||||||
|
) -> sp.CompletedProcess[str]:
|
||||||
|
if use_sudo:
|
||||||
|
pw = password()
|
||||||
|
return sp.run([*pkg_manager, *args], capture_output=True, text=True, input=pw)
|
||||||
|
else:
|
||||||
|
return sp.run([*pkg_manager, *args], capture_output=True, text=True)
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
import list
|
||||||
|
import diff
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
from typing import List
|
||||||
|
import colorama as cl
|
||||||
|
|
||||||
|
from 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,
|
||||||
|
)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
from typing import Any, List
|
||||||
|
import colorama as cl
|
||||||
|
|
||||||
|
|
||||||
|
def count_digits(number: int):
|
||||||
|
return len(str(number))
|
||||||
|
|
||||||
|
|
||||||
|
def print_list(list: List[Any]):
|
||||||
|
digit_count = count_digits(len(list))
|
||||||
|
for i, pkg in enumerate(list):
|
||||||
|
print(
|
||||||
|
" "
|
||||||
|
+ cl.Fore.BLUE
|
||||||
|
+ cl.Style.DIM
|
||||||
|
+ (" " * (digit_count - count_digits(i + 1)))
|
||||||
|
+ str(1 + i)
|
||||||
|
+ cl.Style.RESET_ALL + " ",
|
||||||
|
pkg,
|
||||||
|
)
|
||||||
-64
@@ -1,64 +0,0 @@
|
|||||||
import argparse
|
|
||||||
import argcomplete
|
|
||||||
|
|
||||||
|
|
||||||
def add_cli_args():
|
|
||||||
ap = argparse.ArgumentParser(
|
|
||||||
"archmgr",
|
|
||||||
description="A nixos-like declarative config and package manager for Arch Linux (or any other distro, with some tweaks)",
|
|
||||||
usage="archmgr [command] [options]",
|
|
||||||
)
|
|
||||||
ap.add_argument("-v", "--version", action="version", version="%(prog)s V0.0.1")
|
|
||||||
sp = ap.add_subparsers(
|
|
||||||
title="commands",
|
|
||||||
metavar="Use 'archmgr [command] --help' to see help for each command",
|
|
||||||
dest="cmd",
|
|
||||||
)
|
|
||||||
|
|
||||||
# ┌ ┐
|
|
||||||
# │ Subcommands │
|
|
||||||
# └ ┘
|
|
||||||
commit = sp.add_parser(
|
|
||||||
"commit", help="apply pending changes and commit to git repo"
|
|
||||||
)
|
|
||||||
commit.add_argument("-f", "--force", help="force apply", action="store_true")
|
|
||||||
commit.add_argument(
|
|
||||||
"--no-render", "-r", help="do not re-render renderables", action="store_true"
|
|
||||||
)
|
|
||||||
commit.add_argument(
|
|
||||||
"--dry-run",
|
|
||||||
"-d",
|
|
||||||
help="print out files that would be changed",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
|
|
||||||
sp.add_parser("config", help="prints information about your config")
|
|
||||||
|
|
||||||
init = sp.add_parser("init", help="initialize a new archmgr repository")
|
|
||||||
init.add_argument(
|
|
||||||
"--force",
|
|
||||||
"-f",
|
|
||||||
help="resets the git repository and initializes new archmgr repo",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
|
|
||||||
push = sp.add_parser("push", help="push pending changes to git remote")
|
|
||||||
push.add_argument(
|
|
||||||
"--force",
|
|
||||||
"-f",
|
|
||||||
help="force push (overriding possible remote changes)",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
|
|
||||||
pull = sp.add_parser("pull", help="pull changes from git remote")
|
|
||||||
pull.add_argument("--rebase", "-r", help="execute rebase", action="store_true")
|
|
||||||
pull.add_argument(
|
|
||||||
"--apply",
|
|
||||||
"-a",
|
|
||||||
help="also apply the changes to the local system",
|
|
||||||
action="store_true",
|
|
||||||
)
|
|
||||||
|
|
||||||
argcomplete.autocomplete(ap)
|
|
||||||
|
|
||||||
return ap.parse_args(), ap
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
from commands.util.choice import confirm_overwrite
|
|
||||||
|
|
||||||
|
|
||||||
def commit(dry_run: bool = False, force: bool = False):
|
|
||||||
if dry_run:
|
|
||||||
pass
|
|
||||||
print("Commit, force:", force)
|
|
||||||
print(confirm_overwrite())
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
def config():
|
|
||||||
print(
|
|
||||||
"""
|
|
||||||
Your config can be found at
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import os
|
|
||||||
import commands.util.git as git
|
|
||||||
|
|
||||||
|
|
||||||
def init(force: bool = False):
|
|
||||||
dir = os.getcwd()
|
|
||||||
if force:
|
|
||||||
[os.remove(dir) for dir in os.listdir(dir)]
|
|
||||||
git.init(dir)
|
|
||||||
os.mkdir(dir + "/config")
|
|
||||||
os.mkdir(dir + "/etc")
|
|
||||||
with open(dir + "/config.yaml") as file:
|
|
||||||
file.write("")
|
|
||||||
print("Initialized a new archmgr repository")
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
def pull(rebase: bool = False, apply: bool = False):
|
|
||||||
print("pull")
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
def push(force: bool = False):
|
|
||||||
print("push")
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
from typing import List
|
|
||||||
|
|
||||||
|
|
||||||
def compute_diff(paths: List[str]):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def file_diff(path: str):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def pkg_diff():
|
|
||||||
pass
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import subprocess
|
|
||||||
|
|
||||||
|
|
||||||
def init(dirname: str):
|
|
||||||
subprocess.call("git init", cwd=dirname)
|
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
# Concept for tracking changes
|
# Concepts
|
||||||
- Create new commit (if possible)
|
## Config
|
||||||
- If force is unset: Diff system's files against current state by copying them into the current state branch
|
In python, using functions and args for them
|
||||||
- Then, diff the package state against the state branch by dumping it
|
|
||||||
- Else, or if no diff, continue
|
|
||||||
- Copy normal config files into correct directories
|
## Init
|
||||||
- Render and copy renderables
|
- Copy
|
||||||
- Retrieve explicitly installed packages and remove those that are not present in goal and install those that are not present in current state
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
pyyaml
|
||||||
pylette
|
pylette
|
||||||
argcomplete
|
argcomplete
|
||||||
inquirer
|
inquirer
|
||||||
|
|||||||
Reference in New Issue
Block a user