aboutsummaryrefslogtreecommitdiff
path: root/utils/aur/check-aur-update.py
diff options
context:
space:
mode:
Diffstat (limited to 'utils/aur/check-aur-update.py')
-rw-r--r--utils/aur/check-aur-update.py110
1 files changed, 110 insertions, 0 deletions
diff --git a/utils/aur/check-aur-update.py b/utils/aur/check-aur-update.py
new file mode 100644
index 0000000..85f02c2
--- /dev/null
+++ b/utils/aur/check-aur-update.py
@@ -0,0 +1,110 @@
+import asyncio
+import argparse
+import logging
+from collections.abc import Coroutine
+from pathlib import Path
+from typing import Optional
+
+import aiohttp
+import aiofiles
+
+from AURUtils.srcinfo import SRCINFO
+from AURUtils.version import vercmp
+
+
+MAX_WORKERS = 16
+
+
+async def check_aur_update(
+ session: aiohttp.ClientSession,
+ item: Path, dry_run: bool,
+ parse_pkgbuild: Optional[Path] = None,
+) -> None:
+ if item.name.startswith('.') or not item.is_dir():
+ return
+ pkgbuild_file = item.joinpath('PKGBUILD')
+ if not pkgbuild_file.exists():
+ logging.warning(f'PKGBUILD not in {str(item)}, SKIP!')
+ return
+ srcinfo_file = item.joinpath('.SRCINFO')
+ if not srcinfo_file.exists():
+ if parse_pkgbuild.exists():
+ logging.warning(f'.SRCINFO not in {str(item)}, try to parse')
+ proc = await asyncio.create_subprocess_exec(
+ f'{str(parse_pkgbuild.resolve())}',
+ f'{str(pkgbuild_file)}',
+ stdout=asyncio.subprocess.PIPE,
+ )
+ async with aiofiles.open(srcinfo_file, 'w') as f:
+ await f.write((await proc.stdout.read()).decode())
+ logging.info(f'extract .SRCINFO in {str(item)}')
+ else:
+ logging.warning(f'.SRCINFO not in {str(item)}, no tool to parse')
+ return
+ async with aiofiles.open(srcinfo_file, 'r') as f:
+ srcinfo = SRCINFO.parse(await f.read())
+ version = srcinfo.get_version()
+ async with session.get(f'https://aur.archlinux.org/cgit/aur.git/plain/.SRCINFO?h={srcinfo.pkgbase}') as resp:
+ if resp.status != 200:
+ logging.info(f'{item.name} not register in AUR')
+ return
+ new_version = SRCINFO.parse(await resp.text()).get_version()
+ result = vercmp(new_version, version)
+ if result == 0:
+ return
+ elif result < 0:
+ logging.warning(f'Warning: {version} is newer than AUR')
+ else:
+ if item.joinpath('.git').exists():
+ if not dry_run:
+ await asyncio.create_subprocess_exec('git', '-C', f'{str(item)}', 'pull', 'origin', 'master')
+ logging.info(f'Upgrade {item.name} from {version} to {new_version}')
+ else:
+ logging.info(f'Found update {item.name}({new_version}) (local: {version})')
+
+
+async def with_sem(sem: asyncio.Semaphore, coro: Coroutine):
+ async with sem:
+ return await coro
+
+
+async def main() -> int:
+ parser = argparse.ArgumentParser(description='aur-check-update.py')
+ parser.add_argument('--dry', help='dry run', action='store_true')
+ parser.add_argument('--parse-pkgbuild', dest='script', metavar='SCRIPT',
+ help='a script to parse pkgbuild',
+ action='store')
+ parser.add_argument('--max-workers', dest='workers', metavar='MAX_WORKERS',
+ help=f'aur check max workers, default: {MAX_WORKERS}',
+ default=MAX_WORKERS, action='store', type=int)
+ parser.add_argument(dest='repo_dir', metavar='REPO_DIR', help='repository directory', nargs='?')
+
+ args = parser.parse_args()
+
+ dry_run = args.dry
+ sem = asyncio.Semaphore(args.workers)
+
+ if args.repo_dir is None:
+ logging.error('repository directory not found')
+ return 1
+
+ script_file = Path(args.script)
+ if args.script is None or not (script_file.is_file() and script_file.exists()):
+ logging.info(f'Parse script {str(script_file)+" " if args.script is None else ""} not found')
+ script_file = None
+
+ repo_dir = Path(args.repo_dir)
+
+ async with aiohttp.ClientSession(timeout=aiohttp.ClientTimeout(10)) as session:
+ tasks = [asyncio.create_task(
+ with_sem(sem, check_aur_update(session, item, dry_run, script_file))
+ ) for item in repo_dir.iterdir()]
+ await asyncio.gather(*tasks)
+
+ return 0
+
+
+if __name__ == '__main__':
+ logging.basicConfig(level=logging.DEBUG,
+ format='%(asctime)s - %(levelname)s - %(funcName)s - %(lineno)d - %(message)s')
+ asyncio.run(main()) \ No newline at end of file