diff --git a/README.md b/README.md
index 7907454..35a70c4 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,6 @@ list-old-packages
├── --list-debug
└── repo_path*
run
-├── --delete-debug
├── --dry-run
├── --only-delete
└── repo_path*
@@ -45,11 +44,25 @@ Arguments:
- `--dry-run`: Do a dry run
- `--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.
- - These are needed to run the repo anyways, so you *should* have them installed already.
-- `--delete-debug`: Delete debug symbol packages (default: true)
+ - 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.
+
+## 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
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.
diff --git a/colors.py b/colors.py
new file mode 100644
index 0000000..65dec9e
--- /dev/null
+++ b/colors.py
@@ -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
+ ]
diff --git a/repo-cleanup.py b/repo-cleanup.py
index 3f6cd4a..d42dc94 100755
--- a/repo-cleanup.py
+++ b/repo-cleanup.py
@@ -2,6 +2,9 @@
from natsort import natsorted
import click
import glob
+from subprocess import getstatusoutput
+from colors import colors
+from os import remove
@click.group('cli')
@@ -17,15 +20,70 @@ def cli():
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')
-def run(repo_path, dry_run=False, only_delete=False, delete_debug=False):
- get_old_packages_runner(repo_path)
+def run(repo_path, dry_run=False, only_delete=False):
+ 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')
@@ -60,11 +118,9 @@ def get_old_packages(repo_path, list_debug=True):
old_no_debug = natsorted(no_debug)[:1]
old_packages.extend(old_no_debug)
for item in old_no_debug:
- if (
- list_debug
- and item.replace(pkg_name, f'{pkg_name}-debug') in pkg_map[pkg_name]
- ):
- old_packages.append(item.replace(pkg_name, f'{pkg_name}-debug'))
+ debug_pkg = item.replace(pkg_name, f'{pkg_name}-debug')
+ if list_debug and debug_pkg in pkg_map[pkg_name]:
+ old_packages.append(debug_pkg)
return natsorted(old_packages)
@@ -126,11 +182,11 @@ class HelperFunctions:
Example:
{
- 'swayfx': [
- 'swayfx-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': [
+ 'swayfx-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
+ ]
}
'''
files = HelperFunctions.get_package_list(repo_path)
@@ -180,53 +236,14 @@ class HelperFunctions:
files = [f[f.rfind('/') + 1 :] for f in files]
repo_name = HelperFunctions.get_repo_name(repo_path)
- files.remove(f'{repo_name}.db.tar.zst')
- files.remove(f'{repo_name}.files.tar.zst')
+ try:
+ files.remove(f'{repo_name}.db.tar.zst')
+ files.remove(f'{repo_name}.files.tar.zst')
+ except FileNotFoundError:
+ pass
return natsorted(files)
if __name__ == '__main__':
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()
diff --git a/test.py b/test.py
index 2624e43..faaf3ff 100644
--- a/test.py
+++ b/test.py
@@ -1,6 +1,7 @@
import unittest
import os
from pathlib import Path
+from natsort import natsorted
repo_cleanup = __import__('repo-cleanup')
repo_path = 'repo/'
@@ -33,43 +34,100 @@ def create_test_data():
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):
- def test_list_old(self):
+ def test_run(self):
create_test_data()
- result = repo_cleanup.get_old_packages(repo_path)
- expected = [
- '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_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': [
+ # to run a click command - https://stackoverflow.com/questions/48619517/call-a-click-command-from-code
+ repo_cleanup.run([repo_path, '--only-delete'], standalone_mode=False)
+ result = natsorted(os.listdir(repo_path))
+ expected = 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': [
+ 'example-program-debug-10.2.1-2-x86_64.pkg.tar.zst',
+ 'repo.db',
+ 'repo.db.tar.zst',
+ 'repo.files',
+ 'repo.files.tar.zst',
'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'
]
+ )
+
+ 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)