summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseth <[email protected]>2022-08-21 13:14:00 -0400
committerseth <[email protected]>2022-08-21 13:15:45 -0400
commita615f6cc4901a05f7fe72a55d005280119cb792e (patch)
tree0f3ddc4ac0a27e3a0077e0b79082df17a4e6e5b9
parenta2ff6e94b6ba21818613611cb3fed15e56f4894d (diff)
refactor and (try to) follow PEP518
-rw-r--r--.flake84
-rw-r--r--.gitignore160
-rw-r--r--Makefile14
-rw-r--r--README.md7
-rwxr-xr-xbin/hiccup160
-rw-r--r--default-config.json6
-rwxr-xr-xhiccup.py170
-rw-r--r--pyproject.toml31
-rw-r--r--setup.py2
9 files changed, 371 insertions, 183 deletions
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..0a419e2
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+max-line-length = 88
+extend-ignore =
+ E203 \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..68bc17f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,160 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 4eb5369..0000000
--- a/Makefile
+++ /dev/null
@@ -1,14 +0,0 @@
-shell = /usr/bin/env bash
-PREFIX ?= $(HOME)/.local
-CONFIG = $(HOME)/.config
-ifeq ($(XDG_CONFIG_HOME),)
- CONFIG = $(XDG_CONFIG_HOME)
-endif
-
-install:
- install -Dm755 bin/hiccup $(DESTDIR)$(PREFIX)/bin/hiccup
- install -Dm644 default-config.json $(DESTDIR)$(CONFIG)/hiccup/config.json
-
-uninstall:
- rm $(DESTDIR)$(PREFIX)/bin/hiccup
- rm $(DESTDIR)$(CONFIG)/hiccup/config.json
diff --git a/README.md b/README.md
index b9433a0..ca377e4 100644
--- a/README.md
+++ b/README.md
@@ -19,10 +19,5 @@ see `default-config.json` for example
## how to install
hiccup only needs one command to install :)
```sh
-make install
-```
-
-and to uninstall:
-```sh
-make uninstall
+python setup.py install --user
```
diff --git a/bin/hiccup b/bin/hiccup
deleted file mode 100755
index d65b650..0000000
--- a/bin/hiccup
+++ /dev/null
@@ -1,160 +0,0 @@
-#!/usr/bin/env python3
-import argparse
-import json
-import subprocess
-import os
-
-CONFIG_FILE = os.path.join(os.environ['XDG_CONFIG_HOME'], 'hiccup/config.json')
-OS_RELEASE_PATH = '/etc/os-release'
-
-
-class DistroNotSupportedError(Exception):
- def __init__(self, name):
- self.message = '{} isn\'t supported yet'.format(name)
- super().__init__(self.message)
-
-
-class CurrentDistro:
- __silent = ' > /dev/null 2>&1'
-
- def __init__(self, id: str):
- try:
- with open(CONFIG_FILE) as file:
- data = json.load(file)
- self.__system_update_cmds = data['system_update_cmds']
- self.__extra_cmds = data['extra_cmds']
- self.__clean_cmds = data['clean_cmds']
- self.__shell_plugin_cmds = data['shell_plugin_cmds']
- self.__other_cmds = data['other_cmds']
- except Exception:
- pass
-
- self.id = id
-
- if self.is_supported():
- self.update_cmd = self.get_update_cmd()
- if self.has_clean_cmd():
- self.clean_cmd = self.get_clean_cmd()
- if self.has_extra_cmd():
- self.extra_cmd = self.get_extra_cmd()
- else:
- raise DistroNotSupportedError(self.id)
-
- def __get_cmd(self, dct: dict):
- return dct[self.id]
-
- def __sys_cmd(
- self,
- cmd: str,
- executable='bash',
- append='',
- prepend='',
- sudo=False):
-
- cmd = '{}{}{}'.format(prepend, cmd, append)
- if sudo:
- cmd = 'sudo bash -c \'{}\''.format(cmd)
-
- return subprocess.run(
- cmd,
- check=True,
- executable=executable,
- shell=True)
-
- def __run_items(self, msg: str, dct: dict, name_as_cmd=False):
- executable = 'sh'
- for name, cmd in dct.items():
- print(msg.format(name))
-
- if name_as_cmd:
- executable = name
- self.__sys_cmd(cmd, executable=executable, append=self.__silent)
-
- def is_supported(self):
- return self.id in self.__system_update_cmds
-
- def has_clean_cmd(self):
- return self.id in self.__clean_cmds
-
- def has_extra_cmd(self):
- return self.id in self.__extra_cmds
-
- def get_update_cmd(self):
- return self.__get_cmd(self.__system_update_cmds)
-
- def get_extra_cmd(self):
- return self.__get_cmd(self.__extra_cmds)
-
- def get_clean_cmd(self):
- return self.__get_cmd(self.__clean_cmds)
-
- def update_system(self):
- self.__sys_cmd(self.update_cmd, sudo=True)
- if self.has_extra_cmd():
- self.__sys_cmd(self.extra_cmd)
-
- def cleanup_system(self):
- if self.has_clean_cmd():
- print('cleaning up system...')
- return self.__sys_cmd(self.clean_cmd, sudo=True)
- print('no cleanup command found for {}'.format(self.id))
-
- def update_shell_plugins(self):
- msg = 'updating {} plugins...'
- return self.__run_items(msg, self.__shell_plugin_cmds,
- name_as_cmd=True)
-
- def update_other(self):
- msg = 'updating {}...'
- return self.__run_items(msg, self.__other_cmds)
-
- def update_all(self):
- self.update_system()
- self.update_shell_plugins()
- self.update_other()
- self.cleanup_system()
-
-
-def get_os_release():
- cmd = 'source {}; echo -n $ID'.format(OS_RELEASE_PATH)
- p: subprocess.Popen = subprocess.Popen(
- cmd,
- shell=True,
- executable='bash',
- stdout=subprocess.PIPE)
-
- return str(p.communicate()[0], 'UTF-8').strip()
-
-
-def run():
- current_distro = get_os_release()
- distro = CurrentDistro(current_distro)
-
- parser = argparse.ArgumentParser(
- description='a python script to help keep you up to date')
- parser.add_argument("--cleanonly", "-c", action="store_true",
- default=False, dest="cleanonly",
- help='cleanup unneeded dependencies')
- parser.add_argument("--systemonly", "-s", action="store_true",
- default=False, dest="systemonly",
- help='only update through the system\'s package manager') # noqa: E501
- args = parser.parse_args()
-
- if args.cleanonly:
- return distro.cleanup_system()
- if args.systemonly:
- return distro.update_system()
-
- return distro.update_all()
-
-
-if __name__ == '__main__':
- if os.geteuid() == 0:
- print('please don\'t run this as root :(')
- exit(1)
- try:
- run()
- print('done!')
- except Exception as e:
- print(repr(e))
- exit(2)
diff --git a/default-config.json b/default-config.json
index 4616521..f4a1573 100644
--- a/default-config.json
+++ b/default-config.json
@@ -5,16 +5,16 @@
"fedora": "dnf upgrade"
},
"extra_cmds": {
- "arch": "aur sync --upgrades --rmdeps --sign --remove --verify"
+ "arch": "paru -Sua"
},
"clean_cmds": {
- "arch": "pacman -Rns $(pacman -Qdtq); exit 0",
+ "arch": "paru -c",
"debian": "apt-get --purge autoremove",
"fedora": "dnf autoremove"
},
"shell_plugin_cmds": {
"fish": "fisher update",
- "zsh": "source $HOME/.config/zsh/.antidote/antidote.zsh && antidote update"
+ "zsh": "source $ZDOTDIR/.antidote/antidote.zsh && antidote update"
},
"other_cmds": {
"neovim": "nvim --headless -c 'autocmd User PackerComplete quitall' -c 'PackerSync'"
diff --git a/hiccup.py b/hiccup.py
new file mode 100755
index 0000000..b3f2120
--- /dev/null
+++ b/hiccup.py
@@ -0,0 +1,170 @@
+#!/usr/bin/env python3
+import argparse
+import json
+import subprocess # nosec:b404
+import os
+
+CONFIG_FILE = os.path.join(os.environ["XDG_CONFIG_HOME"], "hiccup/config.json")
+OS_RELEASE_PATH = "/etc/os-release"
+
+
+class DistroNotSupportedError(Exception):
+ def __init__(self, name):
+ self.message = "{} isn't supported yet".format(name)
+ super().__init__(self.message)
+
+
+class Distro:
+ def __init__(self, id: str, config_file: os.path):
+ try:
+ # read and store commands from config file
+ with open(config_file) as file:
+ data = json.load(file)
+ self.__system_update_cmds = data["system_update_cmds"]
+ self.__extra_cmds = data["extra_cmds"]
+ self.__clean_cmds = data["clean_cmds"]
+ self.__shell_plugin_cmds = data["shell_plugin_cmds"]
+ self.__other_cmds = data["other_cmds"]
+ except OSError:
+ raise OSError("no config file found!")
+ except json.JSONDecodeError:
+ raise json.JSONDecodeError("unable to parse json")
+
+ self.id = id
+
+ # get commands specific to current distro
+ if self.is_supported():
+ self.update_cmd = self.get_update_cmd()
+ if self.has_clean_cmd():
+ self.clean_cmd = self.get_clean_cmd()
+ if self.has_extra_cmd():
+ self.extra_cmd = self.get_extra_cmd()
+ else:
+ raise DistroNotSupportedError(self.id)
+
+ def __get_cmd(self, dct: dict):
+ return dct[self.id]
+
+ # wrapper for subprocess.run that allows for easy privlage escalation,
+ # silencing, and variable shells
+ def __sys_cmd(self, cmd: str, shell="bash", silent=False, sudo=False):
+ args = list()
+ if silent:
+ cmd += " > /dev/null 2>&1"
+ if sudo:
+ args += ["/usr/bin/sudo"]
+
+ args += [shell, "-c", cmd]
+
+ return subprocess.run(args, check=True) # nosec:B603
+
+ # iterate through dict of commands, optionally allow for keys to
+ # determine the shell the command is run though
+ def __run_items(self, msg: str, dct: dict, name_as_shell=False):
+ shell = "bash"
+ for name, cmd in dct.items():
+ print(msg.format(name))
+
+ if name_as_shell:
+ shell = name
+ self.__sys_cmd(cmd, shell=shell, silent=True) # nosec:B604
+
+ def is_supported(self):
+ return self.id in self.__system_update_cmds
+
+ def has_clean_cmd(self):
+ return self.id in self.__clean_cmds
+
+ def has_extra_cmd(self):
+ return self.id in self.__extra_cmds
+
+ def get_update_cmd(self):
+ return self.__get_cmd(self.__system_update_cmds)
+
+ def get_extra_cmd(self):
+ return self.__get_cmd(self.__extra_cmds)
+
+ def get_clean_cmd(self):
+ return self.__get_cmd(self.__clean_cmds)
+
+ def update_system(self):
+ self.__sys_cmd(self.update_cmd, sudo=True)
+ if self.has_extra_cmd():
+ self.__sys_cmd(self.extra_cmd)
+
+ def cleanup_system(self):
+ if self.has_clean_cmd():
+ print("cleaning up system...")
+ return self.__sys_cmd(self.clean_cmd, sudo=True)
+ print("no cleanup command found for {}".format(self.id))
+
+ def update_shell_plugins(self):
+ msg = "updating {} plugins..."
+ return self.__run_items(msg, self.__shell_plugin_cmds, name_as_shell=True)
+
+ def update_other(self):
+ msg = "updating {}..."
+ return self.__run_items(msg, self.__other_cmds)
+
+ def update_all(self):
+ self.update_system()
+ self.update_shell_plugins()
+ self.update_other()
+ self.cleanup_system()
+
+
+# reads id from an os-release file
+def get_distro_id(filename: os.path):
+ with open(filename) as file:
+ for line in file.readlines():
+ k, v = line.strip().split("=")
+ if k == "ID":
+ return v
+
+
+def run():
+ try:
+ current_distro = get_distro_id(OS_RELEASE_PATH)
+ except DistroNotSupportedError:
+ pass
+ distro = Distro(current_distro, CONFIG_FILE)
+
+ parser = argparse.ArgumentParser(
+ description="a python script to help keep you up to date"
+ )
+ parser.add_argument(
+ "--cleanonly",
+ "-c",
+ action="store_true",
+ default=False,
+ dest="clean_only",
+ help="cleanup unneeded dependencies",
+ )
+ parser.add_argument(
+ "--systemonly",
+ "-s",
+ action="store_true",
+ default=False,
+ dest="system_only",
+ help="only update through the system's package manager",
+ )
+ args = parser.parse_args()
+
+ if args.clean_only:
+ return distro.cleanup_system()
+ if args.system_only:
+ return distro.update_system()
+
+ return distro.update_all()
+
+
+if __name__ == "__main__":
+ if os.geteuid() == 0:
+ print("please don't run this as root :(")
+ exit(1)
+ try:
+ run()
+ print("done!")
+ except Exception as e:
+ print(repr(e))
+ exit(2)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..2e79d63
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,31 @@
+[build-system]
+requires = ["setuptools>=61.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "hiccup"
+version = "0.0.1"
+authors = [
+ { name = "getchoo", email="[email protected]"},
+]
+description = "a python script to help keep you up to date"
+readme = "README.md"
+license = { file="LICENSE" }
+requires-python = ">=3.7"
+classifiers = [
+ "Programming Language :: Python :: 3",
+ "License :: OSI Approved :: MIT License",
+ "Operating System :: POSIX :: Linux",
+]
+
+[project.urls]
+"Homepage" = "https://github.com/getchoo/hiccup"
+"Bug Tracker" = "https://github.com/getchoo/hiccup/issues"
+
+[project.scripts]
+hiccup = "hiccup:run"
+
+[tool.hatch.build]
+include = [
+ "hiccup.py"
+] \ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..a4f49f9
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,2 @@
+import setuptools
+setuptools.setup()