From 80434f1cfae4bdd08fd79739ccdf8a196bfa0024 Mon Sep 17 00:00:00 2001 From: "Kenneth Benzie (Benie)" Date: Wed, 26 Nov 2025 22:33:15 +0000 Subject: [PATCH] Add tuck module --- library/tuck.py | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 library/tuck.py diff --git a/library/tuck.py b/library/tuck.py new file mode 100644 index 0000000..6496973 --- /dev/null +++ b/library/tuck.py @@ -0,0 +1,121 @@ +# -*- coding: utf-8 -*- + +import os +import subprocess +from os import environ +from pathlib import Path + +import requests +from ansible.module_utils.basic import AnsibleModule + +DOCUMENTATION = """ +module: tuck +author: + - Kenneth Benzie (Benie) +short_description: Ansible kmodule for the tuck tool +options: + name: + description: + - GitHub project name of the package to manage + type: str + state: + description: + - Desired state of the package + type: str + choices: + - "latest" + - "absent" +""" + +EXAMPLES = """ +- name: install package + tuck: + name: kbenzie/tuck + state: present + +- name: uninstall package + tuck: + name: kbenzie/tuck + state: absent +""" + + +def installed(tuck) -> list[str]: + result = subprocess.run( + [tuck, "list", "--quiet"], + capture_output=True, + check=True, + ) + return result.stdout.decode("utf-8").split("\n") + + +def present(tuck: Path, package: str) -> dict: + if package in installed(tuck): + return dict(changed=False) + return latest(tuck, package) + + +def latest(tuck: Path, package: str) -> dict: + result = subprocess.run( + [tuck, "install", package], + capture_output=True, + check=True, + ) + return dict(changed=True, stdout=result.stdout) + + +def absent(tuck: Path, package: str) -> dict: + if package not in installed(tuck): + return dict(changed=False) + result = subprocess.run( + [tuck, "remove", package], + capture_output=True, + check=True, + ) + return dict(changed=True, stdout=result.stdout) + + +def main(): + module = AnsibleModule( + argument_spec=dict( + name=dict(type="str"), + state=dict( + type="str", + choices=[ + "absent", + "latest", + "present", + ], + default="present", + ), + ), + supports_check_mode=False, + ) + + tuck = Path(environ["HOME"]) / ".local" / "bin" / "tuck" + try: + if not (tuck.is_file() and os.access(tuck, os.X_OK)): + response = requests.get("https://kbenzie.github.io/tuck/get.sh") + response.raise_for_status() + subprocess.run( + ["/bin/sh"], + input=response.text.encode("utf-8"), + check=True, + ) + + module.exit_json( + **{ + "absent": absent, + "present": present, + "latest": latest, + }[module.params["state"]](tuck, module.params["name"]) + ) + + except requests.RequestException as error: + module.fail_json(f"failed to install tuck: {error}") + except subprocess.CalledProcessError as error: + module.fail_json(f"failed to install tuck: {error.stderr}") + + +if __name__ == "__main__": + main()