diff --git a/.github/BOTMETA.yml b/.github/BOTMETA.yml index 70781ec654..2801e28ad9 100644 --- a/.github/BOTMETA.yml +++ b/.github/BOTMETA.yml @@ -419,6 +419,8 @@ files: $module_utils/wdc_redfish_utils.py: labels: wdc_redfish_utils maintainers: $team_wdc + $module_utils/xdg_mime.py: + maintainers: mhalano $module_utils/xenserver.py: labels: xenserver maintainers: bvitnik @@ -1447,6 +1449,8 @@ files: maintainers: dinoocch the-maldridge $modules/xcc_: maintainers: panyy3 renxulei + $modules/xdg_mime.py: + maintainers: mhalano $modules/xenserver_: maintainers: bvitnik $modules/xenserver_facts.py: diff --git a/plugins/module_utils/xdg_mime.py b/plugins/module_utils/xdg_mime.py new file mode 100644 index 0000000000..f84b9ef7ea --- /dev/null +++ b/plugins/module_utils/xdg_mime.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2025, Marcos Alano +# Based on gio_mime module. Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt + + +def xdg_mime_runner(module, **kwargs): + return CmdRunner( + module, + command=['xdg-mime'], + arg_formats=dict( + default=cmd_runner_fmt.as_fixed('default'), + query=cmd_runner_fmt.as_fixed('query'), + mime_types=cmd_runner_fmt.as_list(), + handler=cmd_runner_fmt.as_list(), + version=cmd_runner_fmt.as_fixed('--version'), + ), + **kwargs + ) + + +def xdg_mime_get(runner, mime_type): + def process(rc, out, err): + if not out.strip(): + return None + out = out.splitlines()[0] + return out.split()[-1] + + with runner("query default mime_types", output_process=process) as ctx: + return ctx.run(mime_types=mime_type) diff --git a/plugins/modules/xdg_mime.py b/plugins/modules/xdg_mime.py new file mode 100644 index 0000000000..d9494b1667 --- /dev/null +++ b/plugins/modules/xdg_mime.py @@ -0,0 +1,144 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# Copyright (c) 2025, Marcos Alano +# Based on gio_mime module. Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# In memory: This code is dedicated to my late grandmother, Maria Marlene. 1936-2025. Rest in peace, grandma. +# -Marcos Alano- + +# TODO: Add support for diff mode + +from __future__ import absolute_import, division, print_function +__metaclass__ = type + +DOCUMENTATION = r""" +module: xdg_mime +author: + - "Marcos Alano (@mhalano)" +short_description: Set default handler for MIME types, for applications using XDG tools +version_added: 10.7.0 +description: + - This module allows configuring the default handler for specific MIME types when you use applications that rely on XDG. +extends_documentation_fragment: + - community.general.attributes +attributes: + check_mode: + support: full + diff_mode: + support: none +options: + mime_types: + description: + - One or more MIME types for which a default handler will be set. + type: list + elements: str + required: true + handler: + description: + - Sets the default handler for the specified MIME types. + - The desktop file must be installed in the system. + If the desktop file is not installed, the module + does not fail, but the handler is not set either. + - You must pass a handler in the form V(*.desktop), otherwise the module fails. + type: str + required: true +notes: + - This module is a thin wrapper around C(xdg-mime) tool. + - See man xdg-mime(1) for more details. +seealso: + - name: C(xdg-mime) command manual page + description: Manual page for the command. + link: https://portland.freedesktop.org/doc/xdg-mime.html + - name: xdg-utils Documentation + description: Reference documentation for xdg-utils. + link: https://www.freedesktop.org/wiki/Software/xdg-utils/ +""" + +EXAMPLES = r""" +- name: Set Chrome as the default handler for HTTPS + community.general.xdg_mime: + mime_types: x-scheme-handler/https + handler: google-chrome.desktop + register: result + +- name: Set Chrome as the default handler for both HTTP and HTTPS + community.general.xdg_mime: + mime_types: + - x-scheme-handler/http + - x-scheme-handler/https + handler: google-chrome.desktop + register: result +""" + +RETURN = r""" +current_handlers: + description: + - Currently set handlers for the passed MIME types. + returned: success + type: list + elements: str + sample: + - google-chrome.desktop + - firefox.desktop +version: + description: Version of the C(xdg-mime) tool. + type: str + returned: always + sample: "1.2.1" +""" + +from ansible_collections.community.general.plugins.module_utils.module_helper import ModuleHelper +from ansible_collections.community.general.plugins.module_utils.xdg_mime import xdg_mime_runner, xdg_mime_get + + +class XdgMime(ModuleHelper): + output_params = ['handler'] + + module = dict( + argument_spec=dict( + mime_types=dict(type='list', elements='str', required=True), + handler=dict(type='str', required=True), + ), + supports_check_mode=True, + ) + use_old_vardict = False + + def __init_module__(self): + self.runner = xdg_mime_runner(self.module, check_rc=True) + + with self.runner("version") as ctx: + rc, out, err = ctx.run() + self.vars.version = out.replace("xdg-mime ", "").strip() + + if not self.vars.handler.endswith(".desktop"): + self.do_raise(msg="Handler must be a .desktop file") + + self.vars.current_handlers = [] + for mime in self.vars.mime_types: + handler_value = xdg_mime_get(self.runner, mime) + if not handler_value: + handler_value = '' + self.vars.current_handlers.append(handler_value) + + def __run__(self): + check_mode_return = (0, 'Module executed in check mode', '') + + if any(h != self.vars.handler for h in self.vars.current_handlers): + self.changed = True + + if self.has_changed(): + with self.runner.context(args_order="default handler mime_types", check_mode_skip=True, check_mode_return=check_mode_return) as ctx: + rc, out, err = ctx.run() + self.vars.stdout = out + self.vars.stderr = err + self.vars.set("run_info", ctx.run_info, verbosity=1) + + +def main(): + XdgMime.execute() + + +if __name__ == '__main__': + main() diff --git a/tests/unit/plugins/modules/test_xdg_mime.py b/tests/unit/plugins/modules/test_xdg_mime.py new file mode 100644 index 0000000000..b897777632 --- /dev/null +++ b/tests/unit/plugins/modules/test_xdg_mime.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2025, Marcos Alano +# Based on gio_mime module. Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +from ansible_collections.community.general.plugins.modules import xdg_mime +from .uthelper import UTHelper, RunCommandMock + + +UTHelper.from_module(xdg_mime, __name__, mocks=[RunCommandMock]) diff --git a/tests/unit/plugins/modules/test_xdg_mime.yaml b/tests/unit/plugins/modules/test_xdg_mime.yaml new file mode 100644 index 0000000000..83bc15f901 --- /dev/null +++ b/tests/unit/plugins/modules/test_xdg_mime.yaml @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2025, Marcos Alano +# Based on gio_mime module. Copyright (c) 2022, Alexei Znamensky +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later + +# TODO: add tests for setting multiple mime types at once +--- +anchors: + environ: &env-def {environ_update: {LANGUAGE: C, LC_ALL: C}, check_rc: true} + input: &input + mime_types: x-scheme-handler/http + handler: google-chrome.desktop + get_version: &get_version + command: [/testbin/xdg-mime, --version] + environ: *env-def + rc: 0 + out: "xdg-mime 1.2.1\n" + err: '' + query_mime_type: &query_mime_type + command: [/testbin/xdg-mime, query, default, x-scheme-handler/http] + environ: *env-def + rc: 0 + out: '' + err: '' + set_handler: &set_handler + command: [/testbin/xdg-mime, default, google-chrome.desktop, x-scheme-handler/http] + environ: *env-def + rc: 0 + out: '' + err: '' +test_cases: + - id: test_set_handler + input: *input + output: + current_handlers: [''] + changed: true + mocks: + run_command: + - *get_version + - *query_mime_type + - *set_handler + - id: test_set_handler_check + input: *input + output: + current_handlers: ['google-chrome.desktop'] + changed: false + flags: + check: true + mocks: + run_command: + - *get_version + - <<: *query_mime_type + out: | + google-chrome.desktop + - id: test_set_handler_idempot + input: *input + output: + current_handlers: ['google-chrome.desktop'] + changed: false + mocks: + run_command: + - *get_version + - <<: *query_mime_type + out: | + google-chrome.desktop + - id: test_set_handler_idempot_check + input: *input + output: + current_handlers: ['google-chrome.desktop'] + changed: false + flags: + check: true + mocks: + run_command: + - *get_version + - <<: *query_mime_type + out: | + google-chrome.desktop + - id: test_set_invalid_handler + input: + <<: *input + handler: google-chrome.desktopX + output: + failed: true + msg: Handler must be a .desktop file + mocks: + run_command: + - *get_version