#!/usr/bin/env python3 # Copyright (C) 2021 KunoiSayami # # This module is part of KunoiSayami/repos and is released under # the AGPL v3 License: https://www.gnu.org/licenses/agpl-3.0.txt # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from __future__ import annotations import asyncio import logging import os import re import sys from dataclasses import dataclass import aiofiles import aiohttp @dataclass class Version: pkgver: list[str] pkgrel: list[str] epoch: int @classmethod def from_str(cls, s: str) -> Version: match = re.search(r"pkgver=(?P.*)", s) if not match: raise ValueError('pkgver keyword not found') else: pkgver = match.group('VER').split('.') match = re.search(r"pkgrel=(?P.*)", s) if not match: pkgrel = ['1'] else: pkgrel = match.group('REL').split('.') match = re.search(r"epoch=(?P\d+)", s) if not match: epoch = 0 else: epoch = int(match.group('EPOCH')) return cls(pkgver, pkgrel, epoch) @classmethod async def from_file(cls, file: str) -> Version: async with aiofiles.open(file) as fin: return cls.from_str(await fin.read()) def __eq__(self, other: Version) -> bool: return all((self.epoch == other.epoch, self.pkgver == self.pkgver, self.pkgrel == other.pkgrel)) def __gt__(self, other: Version) -> bool: if self.epoch > other.epoch: return True if self.pkgver > other.pkgver: return True if self.pkgver == other.pkgver and self.pkgrel > other.pkgrel: return True return False def __ne__(self, other: Version) -> bool: return not self.__eq__(other) def __ge__(self, other: Version) -> bool: return self.__gt__(other) or self.__eq__(other) def __lt__(self, other: Version) -> bool: return not self.__ge__(other) def __str__(self) -> str: prefix = f'{self.epoch}:' if self.epoch > 0 else '' return f'{prefix}{self.pkgver}-{self.pkgrel}' async def main() -> int: dry_run = '--dry' in sys.argv os.chdir(sys.argv[1]) async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(10)) as session: for item in os.listdir(): if item.startswith('.'): continue if not os.path.isdir(item): continue os.chdir(item) version = await Version.from_file('PKGBUILD') async with session.get(f'https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h={item}') as response: new_version = Version.from_str(await response.text()) if new_version == version: continue elif new_version < version: logging.warning('Warning: %s is newer than AUR', version) continue if '.git' in os.listdir(): if not dry_run: await asyncio.create_subprocess_exec('git', 'pull') logging.info('Upgrade %s from %s to %s', item, version, new_version) else: logging.info('Found update %s(%s) (local: %s)', item, new_version, version) return 0 if __name__ == '__main__': if len(sys.argv) == 1: print('Usage:', sys.argv[0], '') exit(1) logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s') loop = asyncio.get_event_loop() exit(loop.run_until_complete(main()))