diff --git a/README.md b/README.md index ad2a221..468721c 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,12 @@ Ansible module to use some AUR helpers. The following helpers are supported and - [yay](https://github.com/Jguer/yay) ## Options -|parameter|required |default |choices |comments| -|--- |--- |--- |--- |---| -|name |no | | |Name or list of names of the package(s) to install or upgrade.| -|upgrade |no |no |yes, no |Whether or not to upgrade whole system.| -|use |no |auto |auto, pacaur, trizen, yaourt, yay |The helper to use, 'auto' uses the first known helper found.| +|parameter |required |default |choices |comments| +|--- |--- |--- |--- |---| +|name |no | | |Name or list of names of the package(s) to install or upgrade.| +|upgrade |no |no |yes, no |Whether or not to upgrade whole system.| +|use |no |auto |auto, pacaur, trizen, yaourt, yay, internal |The helper to use, 'auto' uses the first known helper found, 'internal' uses the internal helper.| +|skip_installed |no |no |yes, no |Skip operations if the package is present.| ### Note Either *name* or *upgrade* is required, both can not be used together. @@ -37,6 +38,11 @@ ln --symbolic ansible-aur/aur.py aur ### Examples Use it in a task, as in the following examples: ``` +# Install trizen using the internal helper, skip if trizen is already installed +- aur: name=trizen use=internal skip_installed=true + become: yes + become_user: user_that_has_nopasswd_in_sudoers_for_pacman_use + # Install - using the first known helper found - aur: name=package_name become: yes diff --git a/aur.py b/aur.py index e5fdaad..a66ad1c 100644 --- a/aur.py +++ b/aur.py @@ -1,22 +1,53 @@ #!/usr/bin/env python from ansible.module_utils.basic import * +import json +import urllib.request +import tarfile +import os +import os.path +import tempfile - -helper_cmd = { +use_cmd = { 'pacaur': ['env', 'LC_ALL=C', 'pacaur', '-S', '--noconfirm', '--noedit', '--needed', '--aur'], 'trizen': ['env', 'LC_ALL=C', 'trizen', '-S', '--noconfirm', '--noedit', '--needed', '--aur'], 'yaourt': ['env', 'LC_ALL=C', 'yaourt', '-S', '--noconfirm', '--needed'], 'yay': ['env', 'LC_ALL=C', 'yay', '-S', '--noconfirm'], + 'internal': ['env', 'LC_ALL=C', 'makepkg', '--syncdeps', '--install', '--noconfirm', '--needed'] } +def package_installed(module, package): + rc, _, _ = module.run_command(['pacman', '-Q', package], check_rc=False) + return rc == 0 + + +def install_internal(module, package): + f = urllib.request.urlopen('https://aur.archlinux.org/rpc/?v=5&type=info&arg={}'.format(package)) + result = json.loads(f.read().decode('utf8')) + if result['resultcount'] != 1: + return (1, '', 'package not found') + result = result['results'][0] + f = urllib.request.urlopen('https://aur.archlinux.org/{}'.format(result['URLPath'])) + current_path = os.getcwd() + with tempfile.TemporaryDirectory() as tmpdir: + os.chdir(tmpdir) + tar_file = '{}.tar.gz'.format(result['Name']) + with open(tar_file, 'wb') as out: + out.write(f.read()) + tar = tarfile.open(tar_file) + tar.extractall() + tar.close() + os.chdir(format(result['Name'])) + rc, out, err = module.run_command(use_cmd['internal'], check_rc=True) + os.chdir(current_path) + return (rc, out, err) + + def upgrade(module, use): - assert use in helper_cmd + assert use in use_cmd - cmd = helper_cmd[use] + ['-u'] - - rc, out, err = module.run_command(cmd, check_rc=True) + rc, out, err = module.run_command(use_cmd[use] + ['-u'], check_rc=True) module.exit_json( changed=not (out == '' or 'there is nothing to do' in out or 'No AUR updates found' in out), @@ -25,20 +56,27 @@ def upgrade(module, use): ) -def install_packages(module, packages, use): - assert use in helper_cmd +def install_packages(module, packages, use, skip_installed): + assert use in use_cmd changed_iter = False - for package in packages: - cmd = helper_cmd[use] + [package] - rc, out, err = module.run_command(cmd, check_rc=True) - changed_iter = changed_iter or not (out == '' or '-- skipping' in out) + for package in packages: + if skip_installed: + if package_installed(module, package): + rc = 0 + continue + if use == 'internal': + rc, out, err = install_internal(module, package) + else: + rc, out, err = module.run_command(use_cmd[use] + [package], check_rc=True) + changed_iter = changed_iter or not (out == '' or '-- skipping' in out or 'there is nothing to do' in out) module.exit_json( changed=changed_iter, - msg='installed package', + msg='installed package' if not rc else err, helper_used=use, + rc=rc, ) @@ -54,7 +92,11 @@ def main(): }, 'use': { 'default': 'auto', - 'choices': ['auto', 'pacaur', 'trizen', 'yaourt', 'yay'], + 'choices': ['auto', 'pacaur', 'trizen', 'yaourt', 'yay', 'internal'], + }, + 'skip_installed': { + 'default': 'no', + 'type': 'bool', }, }, required_one_of=[['name', 'upgrade']], @@ -63,19 +105,21 @@ def main(): params = module.params if params['use'] == 'auto': - for k in helper_cmd: + use = internal + for k in use_cmd: if module.get_bin_path(k, False): - helper = k + use = k break else: - helper = params['use'] + use = params['use'] - if params['upgrade'] and params['name']: - module.fail_json(msg="Upgrade and install must be requested separately") - if params['upgrade']: - upgrade(module, helper) + if params['upgrade'] and (params['name'] or params['skip_installed'] or use == 'internal'): + module.fail_json(msg="Upgrade cannot be used with this option.") else: - install_packages(module, params['name'], helper) + if params['upgrade']: + upgrade(module, use) + else: + install_packages(module, params['name'], use, params['skip_installed']) if __name__ == '__main__':