aboutsummaryrefslogtreecommitdiff
path: root/utils/aur/check-aur-update.py
blob: 85f02c208c4a9a5b33cfea335301a1f5925f97dc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
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())