From 6ca12cb426c69384e69debc41774a8c36d820f7b Mon Sep 17 00:00:00 2001 From: pika Date: Wed, 9 Apr 2025 19:28:19 +0200 Subject: [PATCH] addet checkpkg.py to check packages from different packagemanagers --- checkpkg.py | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ dev/fonts.py | 76 +++++++++++++++++++++ 2 files changed, 262 insertions(+) create mode 100755 checkpkg.py create mode 100755 dev/fonts.py diff --git a/checkpkg.py b/checkpkg.py new file mode 100755 index 0000000..275670a --- /dev/null +++ b/checkpkg.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +import argparse +import urllib.request +import json +import signal +import re +from concurrent.futures import ThreadPoolExecutor, as_completed +from urllib.error import HTTPError, URLError + +# Updated color codes with simpler separator +COLORS = { + 'reset': '\033[0m', + 'bold': '\033[1m', + 'red': '\033[91m', + 'green': '\033[92m', + 'yellow': '\033[93m', + 'header': '\033[94m' # Simpler header color +} + +def colorize(text, color): + if not hasattr(colorize, 'is_tty'): + colorize.is_tty = __import__('sys').stdout.isatty() + return f"{COLORS[color]}{text}{COLORS['reset']}" if colorize.is_tty else text + +signal.signal(signal.SIGINT, lambda s, f: exit(1)) + +REQUEST_HEADERS = { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) checkpkg/1.0', + 'Accept': 'application/json' +} + +PM_MAPPINGS = { + 'apt': ['debian_', 'ubuntu_'], + 'dnf': ['fedora_'], + 'pacman': ['arch'], + 'aur': ['aur'], + 'apk': ['alpine_'], + 'zypper': ['opensuse_'] +} + +REPO_FORMATS = { + 'debian_': "Debian {}", + 'ubuntu_': "Ubuntu {}", + 'fedora_': "Fedora {}", + 'arch': "Arch", + 'aur': "AUR", + 'alpine_': "Alpine {}", + 'opensuse_': "openSUSE {}" +} + +def version_key(version): + """Create a sorting key for version comparison""" + return [ + (0, int(part)) if part.isdigit() else (1, part.lower()) + for part in re.findall(r'(\d+|\D+)', version) + ] + +def get_package_manager(repo): + for pm, patterns in PM_MAPPINGS.items(): + if any(repo.startswith(p) for p in patterns): + return pm + return None + +def format_repository(repo): + for pattern, fmt in REPO_FORMATS.items(): + if repo.startswith(pattern): + parts = repo.split('_') + return fmt.format(parts[1] if len(parts) > 1 else '') + return repo + +def fetch_package_data(package): + try: + req = urllib.request.Request( + f'https://repology.org/api/v1/project/{package}', + headers=REQUEST_HEADERS + ) + with urllib.request.urlopen(req, timeout=10) as response: + return json.load(response) + except HTTPError as e: + if e.code == 403: + print(colorize(f"Error: Repology blocked the request for {package} (try again later)", 'red')) + return None + except Exception: + return None + +def main(): + parser = argparse.ArgumentParser(description='Package search tool') + parser.add_argument('--all', action='store_true') + parser.add_argument('--apt', action='store_true') + parser.add_argument('--dnf', action='store_true') + parser.add_argument('--pacman', action='store_true') + parser.add_argument('--apk', action='store_true') + parser.add_argument('--zypper', action='store_true') + parser.add_argument('--aur', action='store_true') + parser.add_argument('packages', nargs='+') + args = parser.parse_args() + + selected_pms = [pm for pm, flag in [ + ('apt', args.apt or args.all), + ('dnf', args.dnf or args.all), + ('pacman', args.pacman or args.all), + ('apk', args.apk or args.all), + ('zypper', args.zypper or args.all), + ('aur', args.aur or args.all) + ] if flag] + + if not selected_pms: + print(colorize("Error: No package managers selected", 'red')) + return + + results = {} + with ThreadPoolExecutor(max_workers=5) as executor: + futures = {executor.submit(fetch_package_data, pkg): pkg for pkg in args.packages} + for future in as_completed(futures): + pkg = futures[future] + try: + data = future.result() + results[pkg] = data or [] + except Exception as e: + print(colorize(f"Error processing {pkg}: {str(e)}", 'red')) + results[pkg] = [] + + output = {} + for pkg, entries in results.items(): + pm_versions = {pm: {'version': '', 'repos': set(), 'key': []} for pm in selected_pms} + + for entry in entries: + repo = entry.get('repo', '') + version = entry.get('version', 'N/A') + pm = get_package_manager(repo) + + if pm in selected_pms and version != 'N/A': + repo_fmt = format_repository(repo) + current_key = version_key(version) + stored = pm_versions[pm] + + if not stored['key'] or current_key > stored['key']: + stored['version'] = version + stored['repos'] = {repo_fmt} + stored['key'] = current_key + elif current_key == stored['key']: + stored['repos'].add(repo_fmt) + + output[pkg] = {} + for pm in selected_pms: + data = pm_versions[pm] + if data['version']: + repos = ', '.join(sorted(data['repos'])) + output[pkg][pm] = f"{data['version']} ({repos})" + else: + output[pkg][pm] = 'Not found' + + headers = ['Package'] + selected_pms + col_widths = [len(h) for h in headers] + + for pkg in args.packages: + col_widths[0] = max(col_widths[0], len(pkg)) + for i, pm in enumerate(selected_pms, 1): + col_widths[i] = max(col_widths[i], len(output.get(pkg, {}).get(pm, ''))) + + header_row = [colorize(h.ljust(w), 'header') for h, w in zip(headers, col_widths)] + header_line = ' | '.join(header_row) + print(header_line) + + # Simplified single-line separator without color + if colorize.is_tty: + print('-' * (sum(col_widths) + 3 * (len(headers) - 1))) + else: + print('-' * (sum(col_widths) + 3 * (len(headers) - 1))) + + # Print rows + for pkg in args.packages: + row = [colorize(pkg.ljust(col_widths[0]), 'bold')] + versions = output.get(pkg, {pm: 'Not found' for pm in selected_pms}) + + for pm in selected_pms: + version = versions.get(pm, 'Not found') + color = 'green' if version != 'Not found' else 'red' + if 'AUR' in version: + color = 'yellow' + row.append(colorize(version.ljust(col_widths[headers.index(pm)]), color)) + + print(' | '.join(row)) + +if __name__ == '__main__': + main() diff --git a/dev/fonts.py b/dev/fonts.py new file mode 100755 index 0000000..ea38443 --- /dev/null +++ b/dev/fonts.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python3 + +import os +import subprocess +from concurrent.futures import ThreadPoolExecutor, as_completed +from colorama import init, Fore, Style +import requests + +# Initialize colorama +init(autoreset=True) + +# List of font repositories +FONT_REPOS = { + "JetBrainsMono": "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.3.0/JetBrainsMono.zip", + "CaskaydiaCove": "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.3.0/CascadiaCode.zip", + # Add more repositories here +} + +def display_menu(options, existing_fonts): + print(Fore.CYAN + "Select Font Repositories to Download:") + for i, option in enumerate(options, 1): + icon = Fore.GREEN + "✓" if option in existing_fonts else Fore.RED + " " + print(f"{i}. {option} {icon}") + print(Fore.YELLOW + "0. Exit") + +def get_user_selection(options): + selected_indices = [] + while True: + try: + choices = input(Fore.YELLOW + "Enter the numbers of your choices separated by spaces (q to quit): ") + if choices.strip().lower() == "q": + break + indices = [int(choice.strip()) - 1 for choice in choices.split() if choice.strip().isdigit()] + for index in indices: + if 0 <= index < len(options): + selected_indices.append(index) + else: + print(Fore.RED + f"Invalid choice: {index + 1}. Please try again.") + except ValueError: + print(Fore.RED + "Invalid input. Please enter numbers separated by spaces.") + return selected_indices + +def download_font(url, download_path): + response = requests.get(url) + with open(download_path, 'wb') as file: + file.write(response.content) + +def clone_repos(selected_repos, base_clone_directory): + with ThreadPoolExecutor(max_workers=4) as executor: + future_to_repo = { + executor.submit(download_font, FONT_REPOS[repo], os.path.join(base_clone_directory, f"{repo}.zip")): repo + for repo in selected_repos + } + for future in as_completed(future_to_repo): + repo = future_to_repo[future] + try: + future.result() + print(Fore.GREEN + f"Successfully downloaded {repo}") + except Exception as e: + print(Fore.RED + f"Failed to download {repo}: {e}") + +def check_existing_fonts(clone_directory): + return {repo for repo in FONT_REPOS if os.path.exists(os.path.join(clone_directory, f"{repo}.zip"))} + +def main(): + base_clone_directory = os.path.expanduser("~/.local/share/fonts/") + os.makedirs(base_clone_directory, exist_ok=True) + existing_fonts = check_existing_fonts(base_clone_directory) + options = list(FONT_REPOS.keys()) + display_menu(options, existing_fonts) + selected_indices = get_user_selection(options) + selected_repos = [options[i] for i in selected_indices if options[i] not in existing_fonts] + clone_repos(selected_repos, base_clone_directory) + +if __name__ == "__main__": + main()