finish up parcut v1
- add run() - misc cleanups, like moving colors to its own file - add more tests, and improve the existing ones
This commit is contained in:
parent
ee5b53a6cb
commit
159b042379
4 changed files with 253 additions and 94 deletions
21
README.md
21
README.md
|
@ -16,7 +16,6 @@ list-old-packages
|
||||||
├── --list-debug
|
├── --list-debug
|
||||||
└── repo_path*
|
└── repo_path*
|
||||||
run
|
run
|
||||||
├── --delete-debug
|
|
||||||
├── --dry-run
|
├── --dry-run
|
||||||
├── --only-delete
|
├── --only-delete
|
||||||
└── repo_path*
|
└── repo_path*
|
||||||
|
@ -45,11 +44,25 @@ Arguments:
|
||||||
- `--dry-run`: Do a dry run
|
- `--dry-run`: Do a dry run
|
||||||
- `--only-delete`: Only delete files, don't modify the repo files from them (default: false)
|
- `--only-delete`: Only delete files, don't modify the repo files from them (default: false)
|
||||||
- Without this argument, parcut will try to remove and add the relevant packages using `repo-add` and `repo-remove`, meaning it optionally depends on those programs.
|
- Without this argument, parcut will try to remove and add the relevant packages using `repo-add` and `repo-remove`, meaning it optionally depends on those programs.
|
||||||
- <small>These are needed to run the repo anyways, so you *should* have them installed already.</small>
|
- <small>As long as you're on Arch, this will be installed, as it's part of pacman. Plus, if you're running a repo, you need these anyways.</small>
|
||||||
- `--delete-debug`: Delete debug symbol packages (default: true)
|
|
||||||
|
## Exit codes
|
||||||
|
|
||||||
|
- `0`: Completed successfully
|
||||||
|
- `10`: Failed to remove old package from repo - probably missing write perms on the database.
|
||||||
|
- `11`: Permission denied when trying to delete a package - missing write perms on the package.
|
||||||
|
- `12`: Failed to add new package to repo - again, probably missing write perms on the database.
|
||||||
|
|
||||||
## Notes and credits
|
## Notes and credits
|
||||||
|
|
||||||
This was inspired by [guydunigo/remove_old_arch_pkgs](https://github.com/guydunigo/remove_old_arch_pkgs), which I used at first, but ran into some bugs with.
|
This was inspired by [guydunigo/remove_old_arch_pkgs](https://github.com/guydunigo/remove_old_arch_pkgs), which I used at first, but ran into some bugs with.
|
||||||
|
|
||||||
I also ~~stole~~ borrowed a bit of code from [blend-os/blend](https://github.com/blend-os/blend) for coloring the terminal.
|
I also ~~stole~~ borrowed and modified a bit of code from [blend-os/blend](https://github.com/blend-os/blend) for coloring the terminal.
|
||||||
|
|
||||||
|
## Notes and limitations
|
||||||
|
|
||||||
|
This requires a repo db file to already exist; if the repo doesn't already exist, you can just create an empty file like this instead: `touch reponame.db`
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
This uses `unittest` for testing; tests can be run by running `python3 -m unittest`. Running `test.py` normally will generate data to test the program, which is also generated automatically at the start of every test.
|
||||||
|
|
71
colors.py
Normal file
71
colors.py
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
# copied from https://github.com/blend-os/blend, and modified
|
||||||
|
class colors:
|
||||||
|
reset = '\x1b[0m'
|
||||||
|
bold = '\x1b[01m'
|
||||||
|
disable = '\x1b[02m'
|
||||||
|
underline = '\x1b[04m'
|
||||||
|
reverse = '\x1b[07m'
|
||||||
|
strikethrough = '\x1b[09m'
|
||||||
|
invisible = '\x1b[08m'
|
||||||
|
|
||||||
|
class fg:
|
||||||
|
black = '\x1b[30m'
|
||||||
|
red = '\x1b[31m'
|
||||||
|
green = '\x1b[32m'
|
||||||
|
yellow = '\x1b[33m'
|
||||||
|
blue = '\x1b[34m'
|
||||||
|
magenta = '\x1b[35m'
|
||||||
|
cyan = '\x1b[36m'
|
||||||
|
white = '\x1b[37m'
|
||||||
|
default = '\x1b[39m'
|
||||||
|
|
||||||
|
# bright colors based off this: https://gist.github.com/nfejzic/6f3788b3841cb0d7ac11584c6b33b5b9
|
||||||
|
class bright:
|
||||||
|
black = '\x1b[90m'
|
||||||
|
red = '\x1b[91m'
|
||||||
|
green = '\x1b[92m'
|
||||||
|
yellow = '\x1b[93m'
|
||||||
|
blue = '\x1b[94m'
|
||||||
|
magenta = '\x1b[95m'
|
||||||
|
cyan = '\x1b[96m'
|
||||||
|
white = '\x1b[97m'
|
||||||
|
|
||||||
|
rainbow = [
|
||||||
|
bright.red,
|
||||||
|
yellow,
|
||||||
|
bright.yellow,
|
||||||
|
bright.green,
|
||||||
|
bright.cyan,
|
||||||
|
blue,
|
||||||
|
magenta
|
||||||
|
]
|
||||||
|
|
||||||
|
class bg:
|
||||||
|
black = '\x1b[40m'
|
||||||
|
red = '\x1b[41m'
|
||||||
|
green = '\x1b[42m'
|
||||||
|
yellow = '\x1b[43m'
|
||||||
|
blue = '\x1b[44m'
|
||||||
|
magenta = '\x1b[45m'
|
||||||
|
cyan = '\x1b[46m'
|
||||||
|
white = '\x1b[47m'
|
||||||
|
|
||||||
|
class bright:
|
||||||
|
black = '\x1b[40m'
|
||||||
|
red = '\x1b[41m'
|
||||||
|
green = '\x1b[42m'
|
||||||
|
yellow = '\x1b[43m'
|
||||||
|
blue = '\x1b[44m'
|
||||||
|
magenta = '\x1b[45m'
|
||||||
|
cyan = '\x1b[46m'
|
||||||
|
white = '\x1b[47m'
|
||||||
|
|
||||||
|
rainbow = [
|
||||||
|
bright.red,
|
||||||
|
yellow,
|
||||||
|
bright.yellow,
|
||||||
|
bright.green,
|
||||||
|
bright.cyan,
|
||||||
|
blue,
|
||||||
|
magenta
|
||||||
|
]
|
141
repo-cleanup.py
141
repo-cleanup.py
|
@ -2,6 +2,9 @@
|
||||||
from natsort import natsorted
|
from natsort import natsorted
|
||||||
import click
|
import click
|
||||||
import glob
|
import glob
|
||||||
|
from subprocess import getstatusoutput
|
||||||
|
from colors import colors
|
||||||
|
from os import remove
|
||||||
|
|
||||||
|
|
||||||
@click.group('cli')
|
@click.group('cli')
|
||||||
|
@ -17,15 +20,70 @@ def cli():
|
||||||
default=False,
|
default=False,
|
||||||
help="Only delete files, don't run repo-remove and repo-add on their old and new versions (default: false)"
|
help="Only delete files, don't run repo-remove and repo-add on their old and new versions (default: false)"
|
||||||
)
|
)
|
||||||
@click.option(
|
|
||||||
'--delete-debug',
|
|
||||||
is_flag=True,
|
|
||||||
default=False,
|
|
||||||
help='Delete debug symbol packages (default: false)'
|
|
||||||
)
|
|
||||||
@click.argument('repo_path')
|
@click.argument('repo_path')
|
||||||
def run(repo_path, dry_run=False, only_delete=False, delete_debug=False):
|
def run(repo_path, dry_run=False, only_delete=False):
|
||||||
get_old_packages_runner(repo_path)
|
if dry_run:
|
||||||
|
print(
|
||||||
|
f'{colors.bg.bright.blue}{colors.fg.black}Dry run mode enabled; no changes will be made{colors.reset}\n'
|
||||||
|
)
|
||||||
|
old_packages = get_old_packages(repo_path)
|
||||||
|
repo_name = HelperFunctions.get_repo_name(repo_path)
|
||||||
|
|
||||||
|
if not only_delete:
|
||||||
|
print('=== Removing old packages ===')
|
||||||
|
for pkg in old_packages:
|
||||||
|
print(f'Removing {pkg}...')
|
||||||
|
if not dry_run:
|
||||||
|
output = getstatusoutput(
|
||||||
|
f'repo-remove {repo_path}/{repo_name}.db.tar.zst {repo_path}/{pkg}'
|
||||||
|
)
|
||||||
|
if output[0] != 0:
|
||||||
|
print(
|
||||||
|
f'{colors.bg.red}[ERROR]{colors.reset} failed to remove {pkg}'
|
||||||
|
)
|
||||||
|
print(f'Exit code: {output[0]}')
|
||||||
|
print(f'Command output:\n{output[1]}')
|
||||||
|
exit(10)
|
||||||
|
|
||||||
|
print(
|
||||||
|
f'{colors.fg.green}✅ Successfully removed old packages from repo{colors.reset}'
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print('=== Deleting old packages ===')
|
||||||
|
for pkg in old_packages:
|
||||||
|
print(f'Deleting {pkg}...')
|
||||||
|
if not dry_run:
|
||||||
|
try:
|
||||||
|
remove(f'{repo_path}/{pkg}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'{colors.bg.red}[ERROR]{colors.reset} failed to delete {pkg}')
|
||||||
|
print(f'Error: {e}')
|
||||||
|
exit(11)
|
||||||
|
|
||||||
|
print(f'{colors.fg.green}✅ Successfully deleted packages{colors.reset}')
|
||||||
|
print()
|
||||||
|
|
||||||
|
new_packages = get_new_packages(repo_path)
|
||||||
|
if not only_delete:
|
||||||
|
print('=== Adding new packages ===')
|
||||||
|
for pkg in new_packages:
|
||||||
|
print(f'Adding {pkg}...')
|
||||||
|
if not dry_run:
|
||||||
|
output = getstatusoutput(
|
||||||
|
f'repo-add {repo_path}/{repo_name}.db.tar.zst {repo_path}/{pkg}'
|
||||||
|
)
|
||||||
|
if output[0] != 0:
|
||||||
|
print(f'{colors.bg.red}[ERROR]{colors.reset} failed to add {pkg}')
|
||||||
|
print(f'Exit code: {output[0]}')
|
||||||
|
print(f'Command output:\n{output[1]}')
|
||||||
|
exit(12)
|
||||||
|
print(
|
||||||
|
f'{colors.fg.green}✅ Successfully added new packages to repo{colors.reset}'
|
||||||
|
)
|
||||||
|
print()
|
||||||
|
|
||||||
|
print(f'✨ {colors.bg.green}Repo successfully updated{colors.reset} ✨')
|
||||||
|
|
||||||
|
|
||||||
@cli.command('list-old-packages')
|
@cli.command('list-old-packages')
|
||||||
|
@ -60,11 +118,9 @@ def get_old_packages(repo_path, list_debug=True):
|
||||||
old_no_debug = natsorted(no_debug)[:1]
|
old_no_debug = natsorted(no_debug)[:1]
|
||||||
old_packages.extend(old_no_debug)
|
old_packages.extend(old_no_debug)
|
||||||
for item in old_no_debug:
|
for item in old_no_debug:
|
||||||
if (
|
debug_pkg = item.replace(pkg_name, f'{pkg_name}-debug')
|
||||||
list_debug
|
if list_debug and debug_pkg in pkg_map[pkg_name]:
|
||||||
and item.replace(pkg_name, f'{pkg_name}-debug') in pkg_map[pkg_name]
|
old_packages.append(debug_pkg)
|
||||||
):
|
|
||||||
old_packages.append(item.replace(pkg_name, f'{pkg_name}-debug'))
|
|
||||||
|
|
||||||
return natsorted(old_packages)
|
return natsorted(old_packages)
|
||||||
|
|
||||||
|
@ -126,11 +182,11 @@ class HelperFunctions:
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
{
|
{
|
||||||
'swayfx': [
|
'swayfx': [
|
||||||
'swayfx-0.4-3-x86_64.pkg.tar.zst',
|
'swayfx-0.4-3-x86_64.pkg.tar.zst',
|
||||||
'swayfx-debug-0.4-3-x86_64.pkg.tar.zst',
|
'swayfx-debug-0.4-3-x86_64.pkg.tar.zst',
|
||||||
'swayfx-0.3.2-1-x86_64.pkg.tar.zst
|
'swayfx-0.3.2-1-x86_64.pkg.tar.zst
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
'''
|
'''
|
||||||
files = HelperFunctions.get_package_list(repo_path)
|
files = HelperFunctions.get_package_list(repo_path)
|
||||||
|
@ -180,53 +236,14 @@ class HelperFunctions:
|
||||||
files = [f[f.rfind('/') + 1 :] for f in files]
|
files = [f[f.rfind('/') + 1 :] for f in files]
|
||||||
repo_name = HelperFunctions.get_repo_name(repo_path)
|
repo_name = HelperFunctions.get_repo_name(repo_path)
|
||||||
|
|
||||||
files.remove(f'{repo_name}.db.tar.zst')
|
try:
|
||||||
files.remove(f'{repo_name}.files.tar.zst')
|
files.remove(f'{repo_name}.db.tar.zst')
|
||||||
|
files.remove(f'{repo_name}.files.tar.zst')
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
|
||||||
return natsorted(files)
|
return natsorted(files)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
cli()
|
cli()
|
||||||
|
|
||||||
|
|
||||||
class colors:
|
|
||||||
reset = '\x1b[0m'
|
|
||||||
bold = '\x1b[01m'
|
|
||||||
disable = '\x1b[02m'
|
|
||||||
underline = '\x1b[04m'
|
|
||||||
reverse = '\x1b[07m'
|
|
||||||
strikethrough = '\x1b[09m'
|
|
||||||
invisible = '\x1b[08m'
|
|
||||||
|
|
||||||
class fg:
|
|
||||||
black = '\x1b[30m'
|
|
||||||
red = '\x1b[31m'
|
|
||||||
green = '\x1b[32m'
|
|
||||||
orange = '\x1b[33m'
|
|
||||||
blue = '\x1b[34m'
|
|
||||||
purple = '\x1b[35m'
|
|
||||||
cyan = '\x1b[36m'
|
|
||||||
lightgrey = '\x1b[37m'
|
|
||||||
darkgrey = '\x1b[90m'
|
|
||||||
lightred = '\x1b[91m'
|
|
||||||
lightgreen = '\x1b[92m'
|
|
||||||
yellow = '\x1b[93m'
|
|
||||||
lightblue = '\x1b[94m'
|
|
||||||
pink = '\x1b[95m'
|
|
||||||
lightcyan = '\x1b[96m'
|
|
||||||
|
|
||||||
rainbow = [lightred, orange, yellow, lightgreen, lightcyan, blue, purple]
|
|
||||||
|
|
||||||
class bg:
|
|
||||||
black = '\x1b[40m'
|
|
||||||
red = '\x1b[41m'
|
|
||||||
green = '\x1b[42m'
|
|
||||||
orange = '\x1b[43m'
|
|
||||||
blue = '\x1b[44m'
|
|
||||||
purple = '\x1b[45m'
|
|
||||||
cyan = '\x1b[46m'
|
|
||||||
lightgrey = '\x1b[47m'
|
|
||||||
|
|
||||||
|
|
||||||
fg = colors.fg()
|
|
||||||
|
|
114
test.py
114
test.py
|
@ -1,6 +1,7 @@
|
||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from natsort import natsorted
|
||||||
|
|
||||||
repo_cleanup = __import__('repo-cleanup')
|
repo_cleanup = __import__('repo-cleanup')
|
||||||
repo_path = 'repo/'
|
repo_path = 'repo/'
|
||||||
|
@ -33,43 +34,100 @@ def create_test_data():
|
||||||
touch(f'{repo_path}/{f}')
|
touch(f'{repo_path}/{f}')
|
||||||
|
|
||||||
|
|
||||||
|
# NOTE: Add lists are wrapped in natsorted() so that they can be easily manually edited,
|
||||||
|
# rather than having to run and update them manually each time a test is updated
|
||||||
class Tests(unittest.TestCase):
|
class Tests(unittest.TestCase):
|
||||||
def test_list_old(self):
|
def test_run(self):
|
||||||
create_test_data()
|
create_test_data()
|
||||||
result = repo_cleanup.get_old_packages(repo_path)
|
# to run a click command - https://stackoverflow.com/questions/48619517/call-a-click-command-from-code
|
||||||
expected = [
|
repo_cleanup.run([repo_path, '--only-delete'], standalone_mode=False)
|
||||||
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
result = natsorted(os.listdir(repo_path))
|
||||||
'test-debug-r87.e176baf-1-x86_64.pkg.tar.zst',
|
expected = natsorted(
|
||||||
'test-r87.e176baf-1-x86_64.pkg.tar.zst'
|
[
|
||||||
]
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
def test_list_new(self):
|
|
||||||
create_test_data()
|
|
||||||
result = repo_cleanup.get_new_packages(repo_path)
|
|
||||||
expected = [
|
|
||||||
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
|
||||||
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst',
|
|
||||||
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
|
||||||
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
|
||||||
]
|
|
||||||
self.assertEqual(result, expected)
|
|
||||||
|
|
||||||
def test_package_map(self):
|
|
||||||
create_test_data()
|
|
||||||
result = repo_cleanup.HelperFunctions.package_map(repo_path)
|
|
||||||
expected = {
|
|
||||||
'example-program': [
|
|
||||||
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
||||||
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst'
|
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
],
|
'repo.db',
|
||||||
'test': [
|
'repo.db.tar.zst',
|
||||||
|
'repo.files',
|
||||||
|
'repo.files.tar.zst',
|
||||||
'test-debug-r87.e176baf-1-x86_64.pkg.tar.zst',
|
'test-debug-r87.e176baf-1-x86_64.pkg.tar.zst',
|
||||||
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
||||||
'test-r87.e176baf-1-x86_64.pkg.tar.zst',
|
'test-r87.e176baf-1-x86_64.pkg.tar.zst',
|
||||||
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_list_old(self):
|
||||||
|
create_test_data()
|
||||||
|
result = repo_cleanup.get_old_packages(repo_path)
|
||||||
|
expected = natsorted(
|
||||||
|
[
|
||||||
|
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
||||||
|
'test-debug-r87.e176baf-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-r87.e176baf-1-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_list_old_no_debug(self):
|
||||||
|
create_test_data()
|
||||||
|
result = repo_cleanup.get_old_packages(repo_path, list_debug=False)
|
||||||
|
expected = natsorted(
|
||||||
|
[
|
||||||
|
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
||||||
|
'test-r87.e176baf-1-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_list_new(self):
|
||||||
|
create_test_data()
|
||||||
|
result = repo_cleanup.get_new_packages(repo_path)
|
||||||
|
expected = natsorted(
|
||||||
|
[
|
||||||
|
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
|
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
|
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_list_new_no_debug(self):
|
||||||
|
create_test_data()
|
||||||
|
result = repo_cleanup.get_new_packages(repo_path)
|
||||||
|
expected = natsorted(
|
||||||
|
[
|
||||||
|
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
|
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
|
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
def test_package_map(self):
|
||||||
|
create_test_data()
|
||||||
|
result = repo_cleanup.HelperFunctions.package_map(repo_path)
|
||||||
|
# only the lists are sorted, not the keys, so it should be manually run then verified it's correct if the keys are updated
|
||||||
|
# you can change the items in the list safely, though
|
||||||
|
expected = {
|
||||||
|
'example-program': natsorted(
|
||||||
|
[
|
||||||
|
'example-program-8.4.3-5-x86_64.pkg.tar.zst',
|
||||||
|
'example-program-10.2.1-2-x86_64.pkg.tar.zst',
|
||||||
|
'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
),
|
||||||
|
'test': natsorted(
|
||||||
|
[
|
||||||
|
'test-debug-r87.e176baf-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-debug-r100.ab937ef-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-r87.e176baf-1-x86_64.pkg.tar.zst',
|
||||||
|
'test-r100.ab937ef-1-x86_64.pkg.tar.zst'
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
self.assertEqual(result, expected)
|
self.assertEqual(result, expected)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue