From 1c1f2132c50eb5b95ed5238bf027b464749a5225 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Fri, 20 Feb 2026 19:16:07 +0200 Subject: [PATCH 01/10] Add pgp_keyring action plugin to install PGP keys Add action plugin to install PGP keys. First use case is to convert a PGP keyring to binary format before installing on destination host. --- plugins/action/pgp_keyring.py | 103 ++++++++++++++++++ plugins/modules/pgp_keyring.py | 41 +++++++ tests/integration/requirements.txt | 8 ++ .../targets/pgp_keyring/files/microsoft.asc | 19 ++++ .../targets/pgp_keyring/tasks/main.yml | 21 ++++ 5 files changed, 192 insertions(+) create mode 100644 plugins/action/pgp_keyring.py create mode 100644 plugins/modules/pgp_keyring.py create mode 100644 tests/integration/requirements.txt create mode 100644 tests/integration/targets/pgp_keyring/files/microsoft.asc create mode 100644 tests/integration/targets/pgp_keyring/tasks/main.yml diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py new file mode 100644 index 0000000000..e06b06931f --- /dev/null +++ b/plugins/action/pgp_keyring.py @@ -0,0 +1,103 @@ +# Copyright: (c) 2025, Eero Aaltonen +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + + +from __future__ import annotations + +import os +import shutil +import tempfile + +from ansible import constants as C + +from ansible.config.manager import ensure_type +from ansible.errors import AnsibleActionFail, AnsibleError +from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native +from ansible.module_utils.parsing.convert_bool import boolean +from ansible.plugins.action import ActionBase + +try: + import pgpy +except Exception as e: + raise AnsibleError('PGPym~=0.6.1 must be installed to use pgp_keyring plugin') from e + + +class ActionModule(ActionBase): + + TRANSFERS_FILES = True + + def run(self, tmp=None, task_vars=None): + """ Install PGP keyrings in binary format """ + + if task_vars is None: + task_vars = dict() + + super(ActionModule, self).run(tmp, task_vars) + del tmp # tmp no longer has any effect + + # Options type validation + # strings + for s_type in ('src', 'dest'): + if s_type in self._task.args: + value = ensure_type(self._task.args[s_type], 'string') + if value is not None and not isinstance(value, str): + raise AnsibleActionFail("%s is expected to be a string, but got %s instead" % (s_type, type(value))) + self._task.args[s_type] = value + + # booleans + try: + follow = boolean(self._task.args.get('follow', False), strict=False) + except TypeError as e: + raise AnsibleActionFail(to_native(e)) + + # assign to local vars for ease of use + source = self._task.args.get('src', None) + dest = self._task.args.get('dest', None) + + try: + # logical validation + if source is None or dest is None: + raise AnsibleActionFail("src and dest are required") + + try: + # find in expected paths + source = self._find_needle('files', source) + except AnsibleError as e: + raise AnsibleActionFail(to_text(e)) + + try: + key, po = pgpy.PGPKey.from_file(source) + except FileNotFoundError as e: + raise AnsibleActionFail("could not find src=%s, %s" % (source, to_text(e))) + except Exception as e: + raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) + + new_task = self._task.copy() + local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) + + try: + result_file = os.path.join(local_tempdir, os.path.basename(source)) + with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f: + f.write(bytes(key)) + + new_task.args.update( + dict( + src=result_file, + dest=dest, + follow=follow, + ), + ) + # call with ansible.legacy prefix to eliminate collisions with collections while still allowing local override + copy_action = self._shared_loader_obj.action_loader.get('ansible.legacy.copy', + task=new_task, + connection=self._connection, + play_context=self._play_context, + loader=self._loader, + templar=self._templar, + shared_loader_obj=self._shared_loader_obj) + return copy_action.run(task_vars=task_vars) + finally: + shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict')) + + finally: + self._remove_tmp_path(self._connection._shell.tmpdir) diff --git a/plugins/modules/pgp_keyring.py b/plugins/modules/pgp_keyring.py new file mode 100644 index 0000000000..7cc3166f73 --- /dev/null +++ b/plugins/modules/pgp_keyring.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Copyright: (c) 2025, Eero Aaltonen +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +# This module is implemented as an action plugin and runs on the controller + +from __future__ import annotations +from __future__ import (absolute_import, division, print_function) +__metaclass__ = type + + +DOCUMENTATION = r""" +--- +module: pgp_keyring +short_description: Install PGP keyrings in binary format +description: Converts PGP keyrings to binary format on the ansible controller, + and installs them to the target systems. +version_added: 12.4.0 +author: "Eero Aaltonen (@eaaltonen)" +options: + src: + description: Source key file (typically ASCII armored) + required: true + type: str + dest: + description: Destination key file. Can be relative, in which case the target system default is used + required: true + type: str + follow: + description: This flag indicates that filesystem links in the destination, if they exist, should be followed. + type: bool + default: false +""" + +EXAMPLES = r""" +- name: Install Microsoft Package signing key + community.general.pgp_keyring: + src: microsoft.asc + dest: microsoft.gpg + become: true +""" diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt new file mode 100644 index 0000000000..f4d81333ac --- /dev/null +++ b/tests/integration/requirements.txt @@ -0,0 +1,8 @@ +# Copyright (c) Ansible Project +# 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 + + +# requirement for the pgp_keyring action plugin +PGPy == 0.6.0 ; python_version <= '3.12' +PGPym ~= 0.6.1 ; python_version >= '3.13' diff --git a/tests/integration/targets/pgp_keyring/files/microsoft.asc b/tests/integration/targets/pgp_keyring/files/microsoft.asc new file mode 100644 index 0000000000..1b619e6dc8 --- /dev/null +++ b/tests/integration/targets/pgp_keyring/files/microsoft.asc @@ -0,0 +1,19 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: BSN Pgp v1.1.0.0 + +mQENBFYxWIwBCADAKoZhZlJxGNGWzqV+1OG1xiQeoowKhssGAKvd+buXCGISZJwT +LXZqIcIiLP7pqdcZWtE9bSc7yBY2MalDp9Liu0KekywQ6VVX1T72NPf5Ev6x6DLV +7aVWsCzUAF+eb7DC9fPuFLEdxmOEYoPjzrQ7cCnSV4JQxAqhU4T6OjbvRazGl3ag +OeizPXmRljMtUUttHQZnRhtlzkmwIrUivbfFPD+fEoHJ1+uIdfOzZX8/oKHKLe2j +H632kvsNzJFlROVvGLYAk2WRcLu+RjjggixhwiB+Mu/A8Tf4V6b+YppS44q8EvVr +M+QvY7LNSOffSO6Slsy9oisGTdfE39nC7pVRABEBAAG0N01pY3Jvc29mdCAoUmVs +ZWFzZSBzaWduaW5nKSA8Z3Bnc2VjdXJpdHlAbWljcm9zb2Z0LmNvbT6JATQEEwEI +AB4FAlYxWIwCGwMGCwkIBwMCAxUIAwMWAgECHgECF4AACgkQ6z6Urb4SKc+P9gf/ +diY2900wvWEgV7iMgrtGzx79W/PbwWiOkKoD9sdzhARXWiP8Q5teL/t5TUH6TZ3B +ENboDjwr705jLLPwuEDtPI9jz4kvdT86JwwG6N8gnWM8Ldi56SdJEtXrzwtlB/Fe +6tyfMT1E/PrJfgALUG9MWTIJkc0GhRJoyPpGZ6YWSLGXnk4c0HltYKDFR7q4wtI8 +4cBu4mjZHZbxIO6r8Cci+xxuJkpOTIpr4pdpQKpECM6x5SaT2gVnscbN0PE19KK9 +nPsBxyK4wW0AvAhed2qldBPTipgzPhqB2gu0jSryil95bKrSmlYJd1Y1XfNHno5D +xfn5JwgySBIdWWvtOI05gw== +=zPfd +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/integration/targets/pgp_keyring/tasks/main.yml b/tests/integration/targets/pgp_keyring/tasks/main.yml new file mode 100644 index 0000000000..e707dd4435 --- /dev/null +++ b/tests/integration/targets/pgp_keyring/tasks/main.yml @@ -0,0 +1,21 @@ +# test code for the pgp_keyring action +# (c) 2025, Eero Aaltonen + +- name: Install Microsoft Package signing key + pgp_keyring: + src: microsoft.asc + dest: microsoft.pgp + +- name: stat output file + stat: + path: microsoft.pgp + register: binary_keyring + +- assert: + that: + binary_keyring.stat.exists + +- name: Remove binary keyring + file: + path: binary_keyring.path + state: absent From 583aed7bab5ea3607fbd794c84ec4db5d89b77ae Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 12:48:20 +0200 Subject: [PATCH 02/10] fixup update copyright notices --- plugins/action/pgp_keyring.py | 2 +- plugins/modules/pgp_keyring.py | 2 +- tests/integration/targets/pgp_keyring/tasks/main.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index e06b06931f..f5c410fef6 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -1,4 +1,4 @@ -# Copyright: (c) 2025, Eero Aaltonen +# Copyright: (c) 2025–2026, Eero Aaltonen # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) diff --git a/plugins/modules/pgp_keyring.py b/plugins/modules/pgp_keyring.py index 7cc3166f73..9c888dbc57 100644 --- a/plugins/modules/pgp_keyring.py +++ b/plugins/modules/pgp_keyring.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright: (c) 2025, Eero Aaltonen +# Copyright: (c) 2025–2026, Eero Aaltonen # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) # This module is implemented as an action plugin and runs on the controller diff --git a/tests/integration/targets/pgp_keyring/tasks/main.yml b/tests/integration/targets/pgp_keyring/tasks/main.yml index e707dd4435..9ae5208247 100644 --- a/tests/integration/targets/pgp_keyring/tasks/main.yml +++ b/tests/integration/targets/pgp_keyring/tasks/main.yml @@ -1,5 +1,5 @@ # test code for the pgp_keyring action -# (c) 2025, Eero Aaltonen +# (c) 2025–2026, Eero Aaltonen - name: Install Microsoft Package signing key pgp_keyring: From 47429d67c43d96ba9a45ac827ba3d7e95a66e981 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 12:59:07 +0200 Subject: [PATCH 03/10] fixup tests add aliases file and runme.yml --- tests/integration/targets/pgp_keyring/aliases | 5 +++++ tests/integration/targets/pgp_keyring/runme.yml | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/integration/targets/pgp_keyring/aliases create mode 100644 tests/integration/targets/pgp_keyring/runme.yml diff --git a/tests/integration/targets/pgp_keyring/aliases b/tests/integration/targets/pgp_keyring/aliases new file mode 100644 index 0000000000..343f119da8 --- /dev/null +++ b/tests/integration/targets/pgp_keyring/aliases @@ -0,0 +1,5 @@ +# Copyright (c) Ansible Project +# 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 + +azp/posix/3 diff --git a/tests/integration/targets/pgp_keyring/runme.yml b/tests/integration/targets/pgp_keyring/runme.yml new file mode 100644 index 0000000000..8da2e2578b --- /dev/null +++ b/tests/integration/targets/pgp_keyring/runme.yml @@ -0,0 +1,8 @@ +--- +# Copyright (c) Ansible Project +# 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 + +- hosts: localhost + roles: + - {role: pgp_keyring} From e301eb11c29e6b9b2a95f5ff4f62de61335e0d73 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 13:13:04 +0200 Subject: [PATCH 04/10] fixup install dependency on test specific runme.sh --- tests/integration/requirements.txt | 8 -------- tests/integration/targets/pgp_keyring/runme.sh | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) delete mode 100644 tests/integration/requirements.txt create mode 100755 tests/integration/targets/pgp_keyring/runme.sh diff --git a/tests/integration/requirements.txt b/tests/integration/requirements.txt deleted file mode 100644 index f4d81333ac..0000000000 --- a/tests/integration/requirements.txt +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) Ansible Project -# 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 - - -# requirement for the pgp_keyring action plugin -PGPy == 0.6.0 ; python_version <= '3.12' -PGPym ~= 0.6.1 ; python_version >= '3.13' diff --git a/tests/integration/targets/pgp_keyring/runme.sh b/tests/integration/targets/pgp_keyring/runme.sh new file mode 100755 index 0000000000..5991cbb52f --- /dev/null +++ b/tests/integration/targets/pgp_keyring/runme.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +# Copyright (c) Ansible Project +# 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 + +set -eux + +source virtualenv.sh + +# Requirements have to be installed prior to running ansible-playbook +# because plugins and requirements are loaded before the task runs + +pip show -qq PGPy || \ +pip install PGPym + +ANSIBLE_ROLES_PATH=../ ansible-playbook runme.yml "$@" From 8a38027c40d5d3db7d623f01498478e4d9023bb7 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 23:12:54 +0200 Subject: [PATCH 05/10] fixup: license header and Python2isms Remove obvious Python2isms. If I missed something, a list of things to avoid would be appreciated. --- plugins/action/pgp_keyring.py | 4 ++-- plugins/modules/pgp_keyring.py | 9 ++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index f5c410fef6..48901117fc 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -1,6 +1,6 @@ # Copyright: (c) 2025–2026, Eero Aaltonen -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - +# 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 annotations diff --git a/plugins/modules/pgp_keyring.py b/plugins/modules/pgp_keyring.py index 9c888dbc57..fb4473da12 100644 --- a/plugins/modules/pgp_keyring.py +++ b/plugins/modules/pgp_keyring.py @@ -1,13 +1,8 @@ -# -*- coding: utf-8 -*- # Copyright: (c) 2025–2026, Eero Aaltonen -# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) - -# This module is implemented as an action plugin and runs on the controller +# 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 annotations -from __future__ import (absolute_import, division, print_function) -__metaclass__ = type - DOCUMENTATION = r""" --- From 5ae85488a2b25546aed039dc684baee92e5b6f67 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 23:19:57 +0200 Subject: [PATCH 06/10] fixup use named temporary file --- plugins/action/pgp_keyring.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index 48901117fc..28891e8b3f 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -73,31 +73,28 @@ class ActionModule(ActionBase): raise AnsibleActionFail("%s: %s" % (type(e).__name__, to_text(e))) new_task = self._task.copy() - local_tempdir = tempfile.mkdtemp(dir=C.DEFAULT_LOCAL_TMP) - try: - result_file = os.path.join(local_tempdir, os.path.basename(source)) - with open(to_bytes(result_file, errors='surrogate_or_strict'), 'wb') as f: - f.write(bytes(key)) + with tempfile.NamedTemporaryFile('wb', delete=True, delete_on_close=False) as f: + f.write(bytes(key)) + f.flush() + f.close() new_task.args.update( dict( - src=result_file, + src=f.file.name, dest=dest, follow=follow, ), ) # call with ansible.legacy prefix to eliminate collisions with collections while still allowing local override copy_action = self._shared_loader_obj.action_loader.get('ansible.legacy.copy', - task=new_task, - connection=self._connection, - play_context=self._play_context, - loader=self._loader, - templar=self._templar, - shared_loader_obj=self._shared_loader_obj) + task=new_task, + connection=self._connection, + play_context=self._play_context, + loader=self._loader, + templar=self._templar, + shared_loader_obj=self._shared_loader_obj) return copy_action.run(task_vars=task_vars) - finally: - shutil.rmtree(to_bytes(local_tempdir, errors='surrogate_or_strict')) finally: self._remove_tmp_path(self._connection._shell.tmpdir) From fa795788cd7fa6f78c0aee73299ba8454d5606da Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 1 Mar 2026 23:34:17 +0200 Subject: [PATCH 07/10] fixup handle import errors as instructed in devguide https://docs.ansible.com/projects/ansible-core/devel/dev_guide/testing/sanity/import.html#in-plugins --- plugins/action/pgp_keyring.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index 28891e8b3f..e003918039 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -18,8 +18,10 @@ from ansible.plugins.action import ActionBase try: import pgpy -except Exception as e: - raise AnsibleError('PGPym~=0.6.1 must be installed to use pgp_keyring plugin') from e +except ImportError as imp_exc: + PGPY_IMPORT_ERROR = imp_exc +else: + PGPY_IMPORT_ERROR = None class ActionModule(ActionBase): @@ -29,6 +31,9 @@ class ActionModule(ActionBase): def run(self, tmp=None, task_vars=None): """ Install PGP keyrings in binary format """ + if PGPY_IMPORT_ERROR: + raise AnsibleError('PGPym~=0.6.1 must be installed to use pgp_keyring plugin') from PGPY_IMPORT_ERROR + if task_vars is None: task_vars = dict() From e6ee55be8fafeb6e03fd76bccff1849cd27f8006 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 15 Mar 2026 21:20:31 +0200 Subject: [PATCH 08/10] fixup use validate_argument_spec, remove imports --- plugins/action/pgp_keyring.py | 39 ++++++++++------------------------- 1 file changed, 11 insertions(+), 28 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index e003918039..42b5abc868 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -4,16 +4,10 @@ from __future__ import annotations -import os -import shutil import tempfile -from ansible import constants as C - -from ansible.config.manager import ensure_type from ansible.errors import AnsibleActionFail, AnsibleError -from ansible.module_utils.common.text.converters import to_bytes, to_text, to_native -from ansible.module_utils.parsing.convert_bool import boolean +from ansible.module_utils.common.text.converters import to_text from ansible.plugins.action import ActionBase try: @@ -37,33 +31,22 @@ class ActionModule(ActionBase): if task_vars is None: task_vars = dict() + validation_result, new_module_args = self.validate_argument_spec( + argument_spec = {'src': {'required': True, 'type': 'str'}, + 'dest': {'required': True, 'type': 'str'}, + 'follow': {'type': 'bool', 'default': False} + } + ) + super(ActionModule, self).run(tmp, task_vars) del tmp # tmp no longer has any effect - # Options type validation - # strings - for s_type in ('src', 'dest'): - if s_type in self._task.args: - value = ensure_type(self._task.args[s_type], 'string') - if value is not None and not isinstance(value, str): - raise AnsibleActionFail("%s is expected to be a string, but got %s instead" % (s_type, type(value))) - self._task.args[s_type] = value - - # booleans - try: - follow = boolean(self._task.args.get('follow', False), strict=False) - except TypeError as e: - raise AnsibleActionFail(to_native(e)) - # assign to local vars for ease of use - source = self._task.args.get('src', None) - dest = self._task.args.get('dest', None) + source = new_module_args['src'] + dest = new_module_args['dest'] + follow = new_module_args['follow'] try: - # logical validation - if source is None or dest is None: - raise AnsibleActionFail("src and dest are required") - try: # find in expected paths source = self._find_needle('files', source) From 3b81afb62629409618f26501b5b3988aca57f4d3 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 15 Mar 2026 21:49:58 +0200 Subject: [PATCH 09/10] fixup address pycodestyle complaints, use ansible style --- plugins/action/pgp_keyring.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index 42b5abc868..cee06aec4e 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -32,10 +32,11 @@ class ActionModule(ActionBase): task_vars = dict() validation_result, new_module_args = self.validate_argument_spec( - argument_spec = {'src': {'required': True, 'type': 'str'}, - 'dest': {'required': True, 'type': 'str'}, - 'follow': {'type': 'bool', 'default': False} - } + argument_spec=dict( + src=dict(type='str', required=True), + dest=dict(type='str', required=True), + follow=dict(type='bool', default=False) + ) ) super(ActionModule, self).run(tmp, task_vars) @@ -75,7 +76,8 @@ class ActionModule(ActionBase): ), ) # call with ansible.legacy prefix to eliminate collisions with collections while still allowing local override - copy_action = self._shared_loader_obj.action_loader.get('ansible.legacy.copy', + copy_action = self._shared_loader_obj.action_loader.get( + 'ansible.legacy.copy', task=new_task, connection=self._connection, play_context=self._play_context, From 6bb5bf8dc7637e482eada432475ea71a37f470d3 Mon Sep 17 00:00:00 2001 From: Eero Aaltonen Date: Sun, 15 Mar 2026 22:41:21 +0200 Subject: [PATCH 10/10] fixup remove use of delete_on_close added python 3.12 --- plugins/action/pgp_keyring.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/plugins/action/pgp_keyring.py b/plugins/action/pgp_keyring.py index cee06aec4e..34f702feab 100644 --- a/plugins/action/pgp_keyring.py +++ b/plugins/action/pgp_keyring.py @@ -63,10 +63,9 @@ class ActionModule(ActionBase): new_task = self._task.copy() - with tempfile.NamedTemporaryFile('wb', delete=True, delete_on_close=False) as f: + with tempfile.NamedTemporaryFile('wb', delete=True) as f: f.write(bytes(key)) f.flush() - f.close() new_task.args.update( dict(