commit 4c578142c11fbd4373a7b9d292a1dedaf877c836 Author: Eric Torres Date: Sun Jan 27 20:26:50 2019 -0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..68b0642 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +packaging-scripts +PKGBUILD diff --git a/addpkg.sh b/addpkg.sh new file mode 100644 index 0000000..a96f8cb --- /dev/null +++ b/addpkg.sh @@ -0,0 +1,102 @@ +#!/usr/bin/bash +# Add packages from current directory to a given repository + +readonly scriptname='addpkg' + +# default options +declare -a files +declare -a opts +declare -a sigfiles +clean_cachedir=0 +compression='xz' + +# Error messages +readonly no_compression_entered='No compression type entered.' +readonly nosuchdir='No such directory' + +printHelp() { +cat << EOF +Usage: ${scriptname} [options] [repository] -- [additional repo-add opts] +Options: + -C, --compression-type the compression algorithm the db is using + -c, --clean-cachedir use paccache to clean the cache directory + -R, --remove remove old package files + -s, --sign sign repository file +EOF +} + +while true; do + case "${1}" in + '-c'|'--clean-cachedir') + clean_cachedir=1 + shift + continue + ;; + '-C'|'--compression-type') + case "${2}" in + "") + printf '%s\n' "${no_compression_entered}" >&2 + exit 1 + ;; + *) + compression="${2}" + ;; + esac + shift 2 + continue + ;; + --compression-type=*) + compression="${1#*=}" + [[ -z "${compression}" ]] &&\\ + printf '%s\n' "${no_compression_entered}" >&2 && exit 1 + shift + continue + ;; + '-h'|'--help') + printHelp + exit 0 + ;; + '-r'|'--remove') + opts+=('--remove') + shift + continue + ;; + '-s'|'--sign') + opts+=('--sign') + shift + continue + ;; + --) + shift + break + ;; + -*) + printf '%s\n' "Unknown option: ${1}" + exit 1 + ;; + *) + break + ;; + esac +done + +repo="${1}" && shift +repo_dir="/var/cache/pacman/${repo}" +repo_db="${repo_dir}/${repo}.db.tar.${compression}" +extra_opts=("${@}") + +[[ ! -d "${repo_dir}" ]] && printf '%s\n' "${repo_dir}: ${nosuchdir}" >&2 + +files=(*.pkg.tar.xz) +sigfiles=(*.pkg.tar.xz.sig) + +# run repo-add before moving files +repo-add "${opts[@]}" "${extra_opts[@]}" "${repo_db}" "${files[@]}" + +# move package files and sig files into repository directory +mv "${files[@]}" "${sigfiles[@]}" "${repo_dir}" + +if [[ "${clean_cachedir}" == 1 ]]; then + [[ ! -x '/usr/bin/paccache' ]] && echo 'paccache is not present, skipping' + paccache --verbose --remove --keep 1 --cachedir "${repo_dir}" +fi diff --git a/bin/addpkg b/bin/addpkg new file mode 100644 index 0000000..e69de29 diff --git a/bin/delpkg b/bin/delpkg new file mode 100644 index 0000000..60c7a0e --- /dev/null +++ b/bin/delpkg @@ -0,0 +1,140 @@ +#!/usr/bin/env python +""" Delete packages from a repository. +Functions: + move_pkgfiles(repo, files=None) + repo_add(repo, opts, filelist) + clear_cachedir(repo) +""" + +import argparse +import logging +import pathlib +import subprocess +import sys + +import packaging_scripts.pacmanconf as pacmanconf +import packaging_scripts.pkgfiles as pkgfiles + +console_formatter = logging.Formatter('==> %(level)s %(message)s') +syslog = logging.getLogger(__name__) +syslog.setLevel(logging.DEBUG) + +stdout_handler = logging.StreamHandler(sys.stdout) +stdout_handler.setLevel(logging.INFO) +stdout_handler.setFormatter(console_formatter) + +stderr_handler = logging.StreamHandler() +stderr_handler.setLevel(logging.ERROR) +stderr_handler.setFormatter(console_formatter) + +syslog.addHandler(stdout_handler) +syslog.addHandler(stderr_handler) + + +def del_pkgfiles(repo, pkgs): + """ Remove package files from the repository directory. + Parameters: + repo: the repository to delete from + pkgs: a list of packages to remove + """ + syslog.info('Removing package files from cachedir') + cachedir = pathlib.Path(f'/var/cache/pacman/{repo}') + syslog.debug(f"Cache directory: {cachedir}") + syslog.debug(f"Packages: {pkgs}") + + for pkg in pkgs: + for pkgfile in cachedir.glob(f"{pkg}*.pkg.tar.xz*"): + pkgfile.unlink() + syslog.info(f"Removed {pkgfile}") + + +def repo_remove(repo, pkgs, opts=None): + """ Run repo-remove. + Parameters: + repo: the repository to remove from + pkgs: the names of the packages to remove + opts: the list of options to pass to repo-remove + Raises: + subprocess.CalledProcessError if repo-add failed + """ + syslog.info('Removing packages from database') + syslog.debug(f"Options: {opts}") + syslog.debug(f"Packages: {pkgs}") + db = pathlib.Path(f"/var/cache/pacman/{repo}/{repo}.db.tar.xz") + + cmd = ['repo-remove'] + + if opts is not None: + cmd.extend(opts) + + cmd.append(str(db)) + cmd.extend(pkgs) + syslog.debug(f"Final repo-remove command: {cmd}") + + remove_process = subprocess.run(cmd, + check=True, + capture_output=True, + text=True) + + syslog.debug(remove_process.stdout) + if remove_process.stderr: + syslog.error(remove_process.stderr) + + syslog.info('Finished removing packages from database') + + +def clean_cachedir(repo): + """ Run paccache """ + syslog.info('Running paccache') + subprocess.run(['paccache', + '--remove', + '--verbose', + '--keep=1', + f"--cachedir=/var/cache/pacman/{repo}"]) + syslog.info('Finished running paccache') + + +if __name__ == '__main__': + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--clean-cachedir', + action='store_true', + help='use paccache to clean the cache directory') + parser.add_argument('-s', '--sign', + dest='opts', + action='append_const', + const='--sign', + help='sign repository file') + parser.add_argument('-v', '--verbose', + action='store_true', + help='increase script verbosity') + parser.add_argument('repository', + choices=pacmanconf.list_configured_repos(), + help='the repository to operate on', + metavar='repo') + parser.add_argument('packages', + default=None, + nargs='+', + help='packages to remove') + + args = parser.parse_args() + repo = args.repository + pkgs = args.packages + opts = args.opts + clean = args.clean_cachedir + + if args.verbose: + stdout_handler.setLevel(logging.DEBUG) + + if not pathlib.Path(f"/var/cache/pacman/{args.repository}").is_dir(): + syslog.critical(f"Not a repository: {repo}") + + del_pkgfiles(repo, pkgs) + + try: + repo_remove(repo, pkgs, opts) + except subprocess.CalledProcessError as e: + syslog.error(e) + exit(2) + + if clean: + clean_cachedir(repo) diff --git a/bin/fqo b/bin/fqo new file mode 100644 index 0000000..2775836 --- /dev/null +++ b/bin/fqo @@ -0,0 +1,44 @@ +#!/usr/bin/bash +# Fuzzy find a file and then check which package owns it + +declare -r scriptname="fqo" + +printHelp() { +cat << helpinfo +${scriptname} - fuzzy find a file and then check which package owns it + +Usage: ${scriptname} [-h] [patterns] + +Options: + -h, --help print this help page +helpinfo +} + +while true; do + case "${1}" in + "-h"|"--help") + printHelp + exit + ;; + --) + shift + break + ;; + -*) + printf '%s\n' "Unknown option: ${1}" >&2 + exit 1 + ;; + *) + break + ;; + esac +done + +[[ ! -x '/usr/bin/locate' ]] && echo 'locate is not present' && exit 1 +[[ ! -x '/usr/bin/fzf' ]] && echo 'fzf is not present' && exit 1 +[[ -z "${*}" ]] && printf '%s\n' "No patterns entered" >&2 && exit 1 + +file="$(locate --all --ignore-case --null -- "${@}" | fzf --exit-0 --select-1 --read0 --no-mouse)" +[[ ! "${file}" ]] && exit 1 + +pacman -Qo "${file}" diff --git a/delpkg.sh b/delpkg.sh new file mode 100644 index 0000000..0c3ecfc --- /dev/null +++ b/delpkg.sh @@ -0,0 +1,102 @@ +#!/usr/bin/bash +# Add packages from current directory to a given repository + +readonly scriptname='delpkg' + +# default options +declare -a files +declare -a opts +declare -a sigfiles +clean_cachedir=0 +compression='xz' + +# Error messages +readonly no_compression_entered='No compression type entered.' +readonly nosuchdir='No such directory' + +printHelp() { +cat << EOF +Usage: ${scriptname} [options] [repository] -- [additional repo-add opts] +Options: + -C, --compression-type the compression algorithm the db is using + -c, --clean-cachedir use paccache to clean the cache directory + -R, --remove remove old package files + -s, --sign sign repository file +EOF +} + +while true; do + case "${1}" in + '-c'|'--clean-cachedir') + clean_cachedir=1 + shift + continue + ;; + '-C'|'--compression-type') + case "${2}" in + "") + printf '%s\n' "${no_compression_entered}" >&2 + exit 1 + ;; + *) + compression="${2}" + ;; + esac + shift 2 + continue + ;; + --compression-type=*) + compression="${1#*=}" + [[ -z "${compression}" ]] &&\\ + printf '%s\n' "${no_compression_entered}" >&2 && exit 1 + shift + continue + ;; + '-h'|'--help') + printHelp + exit 0 + ;; + '-r'|'--remove') + opts+=('--remove') + shift + continue + ;; + '-s'|'--sign') + opts+=('--sign') + shift + continue + ;; + --) + shift + break + ;; + -*) + printf '%s\n' "Unknown option: ${1}" + exit 1 + ;; + *) + break + ;; + esac +done + +repo="${1}" && shift +repo_dir="/var/cache/pacman/${repo}" +repo_db="${repo_dir}/${repo}.db.tar.${compression}" +pkgnames=("${@}") + +[[ ! -d "${repo_dir}" ]] && printf '%s\n' "${repo_dir}: ${nosuchdir}" >&2 + +files=("${repo_dir}/${name}"*'.pkg.tar.xz') +sigfiles=("${repo_dir}/${name}"*'*.pkg.tar.xz.sig') + +# run repo-remove before removing files +repo-remove "${opts[@]}" "${repo_db}" "${pkgnames[@]}" + +# move package files and sig files into repository directory +mv "${files[@]}" "${sigfiles[@]}" "${repo_dir}" + +if [[ "${clean_cachedir}" == 1 ]]; then + [[ ! -x '/usr/bin/paccache' ]] && echo 'paccache is not present, skipping' + paccache --verbose --remove --keep 1 --cachedir "${repo_dir}" +fi diff --git a/packaging_scripts/__init__.py b/packaging_scripts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packaging_scripts/__pycache__/__init__.cpython-37.pyc b/packaging_scripts/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..738f9f3 Binary files /dev/null and b/packaging_scripts/__pycache__/__init__.cpython-37.pyc differ diff --git a/packaging_scripts/__pycache__/pacmanconf.cpython-37.pyc b/packaging_scripts/__pycache__/pacmanconf.cpython-37.pyc new file mode 100644 index 0000000..74d7d6e Binary files /dev/null and b/packaging_scripts/__pycache__/pacmanconf.cpython-37.pyc differ diff --git a/packaging_scripts/__pycache__/pkgfiles.cpython-37.pyc b/packaging_scripts/__pycache__/pkgfiles.cpython-37.pyc new file mode 100644 index 0000000..69b7eb2 Binary files /dev/null and b/packaging_scripts/__pycache__/pkgfiles.cpython-37.pyc differ diff --git a/packaging_scripts/pacmanconf.py b/packaging_scripts/pacmanconf.py new file mode 100644 index 0000000..d169c56 --- /dev/null +++ b/packaging_scripts/pacmanconf.py @@ -0,0 +1,47 @@ +"""Module for config file helper functions.""" + +import configparser +import pathlib + +PACMAN_CONF = pathlib.Path('/etc/pacman.conf') + + +def parse_configfile(filepath): + """Parse a config file given its path and return + a ConfigParser object. + + :param path: path to config file to parse + :type path: str, bytes, or path-like object + :returns: object used to parse config file + :rtype: ConfigParser object + :raises: FileNotFoundError if path does not exist + """ + if not pathlib.Path(filepath).is_file(): + raise FileNotFoundError(f"{filepath} does not exist") + + config_reader = configparser.ConfigParser(strict=False, + allow_no_value=True) + config_reader.read(filepath) + + return config_reader + + +def list_configured_repos(): + """Read /etc/pacman.conf to list all configured repositories. + + :raises FileNotFoundError: if config file does not exist + :returns: all repos configured on the system + :rtype: list + """ + if not PACMAN_CONF.is_file(): + raise FileNotFoundError(f"{PACMAN_CONF} was not found") + + parsed_config = parse_configfile(PACMAN_CONF) + + repos = parsed_config.sections() + # remove the 'option' entry from the list + del repos[0] + + repos.sort() + + return repos diff --git a/packaging_scripts/pkgfiles.py b/packaging_scripts/pkgfiles.py new file mode 100644 index 0000000..0a0bfb9 --- /dev/null +++ b/packaging_scripts/pkgfiles.py @@ -0,0 +1,24 @@ +"""Helper functions for dealing with package files.""" + +import pathlib + +PKGEXT = 'pkg.tar.xz' +SIGEXT = f"{PKGEXT}.sig" + +def get_pkgfiles(directory=None, signatures=False): + """Return a list of package files in the current working directory. + + :param directory: a directory to search in + :type directory: str, bytes, or path-like object + :returns: paths of package files + :rtype: list + """ + if directory: + path = pathlib.Path(directory) + else: + path = pathlib.Path.cwd() + + if signatures: + return list(path.glob(f"*.{SIGEXT}")) + + return list(path.glob(f"*.{PKGEXT}")) diff --git a/packaging_scripts/zsh-completions/_addpkg b/packaging_scripts/zsh-completions/_addpkg new file mode 100644 index 0000000..7de4ac7 --- /dev/null +++ b/packaging_scripts/zsh-completions/_addpkg @@ -0,0 +1,17 @@ +#compdef addpkg + +# zsh completions for 'addpkg' +# automatically generated with http://github.com/RobSis/zsh-completion-generator +local arguments + +arguments=( + {-h,--help}'[show this help message and exit]' + {-C,--compression-type}'[the compression algorithm the db is using]' + {-c,--clean-cachedir}'[use paccache to clean the cache directory]' + {-R,--remove}'[remove old package files]' + {-s,--sign}'[sign repository file]' + {-v,--verbose}'[increase script verbosity]' + '*:filename:_files' +) + +_arguments -s $arguments diff --git a/packaging_scripts/zsh-completions/_delpkg b/packaging_scripts/zsh-completions/_delpkg new file mode 100644 index 0000000..522beda --- /dev/null +++ b/packaging_scripts/zsh-completions/_delpkg @@ -0,0 +1,15 @@ +#compdef delpkg + +# zsh completions for 'delpkg' +# automatically generated with http://github.com/RobSis/zsh-completion-generator +local arguments + +arguments=( + {-c,--clean-cachedir}'[run paccache to clean the cache directory]' + {-h,--help}'[show this help message and exit]' + {-s,--sign}'[sign repository file]' + {-v, --verbose}'[increase script verbosity]' + '*:filename:_files' +) + +_arguments -s $arguments diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..7694ead --- /dev/null +++ b/setup.py @@ -0,0 +1,27 @@ +import setuptools +import subprocess + +with open("README.rst", "r") as fh: + long_description = fh.read() + +def get_version(): + cmd = 'printf "%s" "$(git describe --long | sed "s/\([^-]*-\)g/r\1/;s/-/./g")"' + return subprocess.run(cmd, shell=True, text=True).stdout + +setuptools.setup( + name="packaging_scripts", + version=get_version(), + author="Eric Russel Torres", + author_email="erictorres4@protonmail.com", + description="A set of helpers for automating borg interaction", + long_description=long_description, + long_description_content_type="text/plain", + url="", + packages=setuptools.find_packages(), + scripts=['bin/delpkg', 'bin/fqo'], + classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: GPL3 License", + "Operating System :: OS Independent", + ], +) diff --git a/zsh-completions/_addpkg b/zsh-completions/_addpkg new file mode 100644 index 0000000..7de4ac7 --- /dev/null +++ b/zsh-completions/_addpkg @@ -0,0 +1,17 @@ +#compdef addpkg + +# zsh completions for 'addpkg' +# automatically generated with http://github.com/RobSis/zsh-completion-generator +local arguments + +arguments=( + {-h,--help}'[show this help message and exit]' + {-C,--compression-type}'[the compression algorithm the db is using]' + {-c,--clean-cachedir}'[use paccache to clean the cache directory]' + {-R,--remove}'[remove old package files]' + {-s,--sign}'[sign repository file]' + {-v,--verbose}'[increase script verbosity]' + '*:filename:_files' +) + +_arguments -s $arguments diff --git a/zsh-completions/_delpkg b/zsh-completions/_delpkg new file mode 100644 index 0000000..522beda --- /dev/null +++ b/zsh-completions/_delpkg @@ -0,0 +1,15 @@ +#compdef delpkg + +# zsh completions for 'delpkg' +# automatically generated with http://github.com/RobSis/zsh-completion-generator +local arguments + +arguments=( + {-c,--clean-cachedir}'[run paccache to clean the cache directory]' + {-h,--help}'[show this help message and exit]' + {-s,--sign}'[sign repository file]' + {-v, --verbose}'[increase script verbosity]' + '*:filename:_files' +) + +_arguments -s $arguments