From 32c4bbea30f5ffebd54ea2abab5dfb8487a386fc Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:30:31 +0200 Subject: [PATCH] [PR #11748/972bed66 backport][stable-12] flatpak: add from_url parameter, deprecate URLs in name (#11814) flatpak: add from_url parameter, deprecate URLs in name (#11748) * flatpak: add from_url parameter, deprecate URLs in name Adds a new `from_url` parameter for installing flatpaks from a .flatpakref URL, using `flatpak install --from `. The `name` parameter then carries the reverse DNS application ID, enabling reliable idempotency checks. Passing URLs directly in `name` is now deprecated and will be removed in community.general 14.0.0. Fixes #4000 * flatpak: add changelog fragment for PR #11748 * flatpak: remove deprecation, adjust docs tone * flatpak: add integration tests for from_url parameter --------- (cherry picked from commit 972bed66f434d1632933f6c010339794dbee4175) Co-authored-by: Alexei Znamensky <103110+russoz@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 --- .../fragments/11748-flatpak-from-url.yml | 4 + plugins/modules/flatpak.py | 59 ++++++++---- .../targets/flatpak/tasks/check_mode.yml | 51 +++++++++++ .../targets/flatpak/tasks/test.yml | 91 +++++++++++++++++++ 4 files changed, 189 insertions(+), 16 deletions(-) create mode 100644 changelogs/fragments/11748-flatpak-from-url.yml diff --git a/changelogs/fragments/11748-flatpak-from-url.yml b/changelogs/fragments/11748-flatpak-from-url.yml new file mode 100644 index 0000000000..419f7ff1e5 --- /dev/null +++ b/changelogs/fragments/11748-flatpak-from-url.yml @@ -0,0 +1,4 @@ +minor_changes: + - flatpak - add new parameter ``from_url`` to install a flatpak from a ``.flatpakref`` URL + (https://github.com/ansible-collections/community.general/issues/4000, + https://github.com/ansible-collections/community.general/pull/11748). diff --git a/plugins/modules/flatpak.py b/plugins/modules/flatpak.py index e3f9ad2466..f87d43d84d 100644 --- a/plugins/modules/flatpak.py +++ b/plugins/modules/flatpak.py @@ -45,18 +45,27 @@ options: name: description: - The name of the flatpak to manage. To operate on several packages this can accept a list of packages. - - When used with O(state=present), O(name) can be specified as a URL to a C(flatpakref) file or the unique reverse DNS - name that identifies a flatpak. - - Both C(https://) and C(http://) URLs are supported. + - Should be specified as the unique reverse DNS name that identifies a flatpak (for example V(org.gnome.gedit)). - When supplying a reverse DNS name, you can use the O(remote) option to specify on what remote to look for the flatpak. - An example for a reverse DNS name is C(org.gnome.gedit). - - When used with O(state=absent) or O(state=latest), it is recommended to specify the name in the reverse DNS format. + - When used with O(state=present), O(name) can also be specified as a C(https://) or C(http://) URL to a C(flatpakref) file. + However, it is recommended you use O(from_url) instead to get reliable idempotency. + Passing URLs in O(name) will be deprecated in the future. + - When used with O(state=absent) or O(state=latest), always specify the name in the reverse DNS format. - When supplying a URL with O(state=absent) or O(state=latest), the module tries to match the installed flatpak based on the name of the flatpakref to remove or update it. However, there is no guarantee that the names of the flatpakref file and the reverse DNS name of the installed flatpak do match. type: list elements: str required: true + from_url: + description: + - A C(http://) or C(https://) URL pointing to a C(.flatpakref) file to install from. + - When this option is set, O(name) must contain exactly one entry specifying the reverse DNS application ID of the + flatpak (for example V(com.onepassword.OnePassword)). This is used to check whether the flatpak is already installed. + - O(name) and O(from_url) cannot both contain URLs. + - This option is recommended instead of passing a URL in O(name); passing URLs in O(name) will be deprecated in the future. + type: str + version_added: "12.6.0" no_dependencies: description: - If installing runtime dependencies should be omitted or not. @@ -84,12 +93,14 @@ options: EXAMPLES = r""" - name: Install the spotify flatpak community.general.flatpak: - name: https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref + name: com.spotify.Client + from_url: https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref state: present - name: Install the gedit flatpak package without dependencies (not recommended) community.general.flatpak: - name: https://git.gnome.org/browse/gnome-apps-nightly/plain/gedit.flatpakref + name: org.gnome.gedit + from_url: https://git.gnome.org/browse/gnome-apps-nightly/plain/gedit.flatpakref state: present no_dependencies: true @@ -120,12 +131,14 @@ EXAMPLES = r""" - name: Update the spotify flatpak community.general.flatpak: - name: https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref + name: com.spotify.Client + from_url: https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref state: latest - name: Update the gedit flatpak package without dependencies (not recommended) community.general.flatpak: - name: https://git.gnome.org/browse/gnome-apps-nightly/plain/gedit.flatpakref + name: org.gnome.gedit + from_url: https://git.gnome.org/browse/gnome-apps-nightly/plain/gedit.flatpakref state: latest no_dependencies: true @@ -180,7 +193,7 @@ from ansible_collections.community.general.plugins.module_utils.version import L OUTDATED_FLATPAK_VERSION_ERROR_MESSAGE = "Unknown option --columns=application" -def install_flat(module, binary, remote, names, method, no_dependencies): +def install_flat(module, binary, remote, names, method, no_dependencies, from_url=None): """Add new flatpaks.""" global result # pylint: disable=global-variable-not-assigned uri_names = [] @@ -198,12 +211,16 @@ def install_flat(module, binary, remote, names, method, no_dependencies): base_command += ["--noninteractive"] if no_dependencies: base_command += ["--no-deps"] - if uri_names: - command = base_command + uri_names - _flatpak_command(module, module.check_mode, command) - if id_names: - command = base_command + [remote] + id_names + if from_url: + command = base_command + ["--from", from_url] _flatpak_command(module, module.check_mode, command) + else: + if uri_names: + command = base_command + uri_names + _flatpak_command(module, module.check_mode, command) + if id_names: + command = base_command + [remote] + id_names + _flatpak_command(module, module.check_mode, command) result["changed"] = True @@ -356,6 +373,7 @@ def main(): module = AnsibleModule( argument_spec=dict( name=dict(type="list", elements="str", required=True), + from_url=dict(type="str"), remote=dict(type="str", default="flathub"), method=dict(type="str", default="system", choices=["user", "system"]), state=dict(type="str", default="present", choices=["absent", "present", "latest"]), @@ -366,6 +384,7 @@ def main(): ) name = module.params["name"] + from_url = module.params["from_url"] state = module.params["state"] remote = module.params["remote"] no_dependencies = module.params["no_dependencies"] @@ -380,6 +399,14 @@ def main(): if not binary: module.fail_json(msg=f"Executable '{executable}' was not found on the system.", **result) + url_names = [n for n in name if n.startswith("http://") or n.startswith("https://")] + if from_url is not None: + if not (from_url.startswith("http://") or from_url.startswith("https://")): + module.fail_json(msg="The 'from_url' parameter must be an http:// or https:// URL.", **result) + if url_names: + module.fail_json(msg="The 'name' and 'from_url' parameters cannot both contain URLs.", **result) + if len(name) != 1: + module.fail_json(msg="When 'from_url' is used, 'name' must contain exactly one entry.", **result) module.run_command_environ_update = dict(LANGUAGE="C", LC_ALL="C") installed, not_installed = flatpak_exists(module, binary, name, method) @@ -389,7 +416,7 @@ def main(): if state == "latest" and installed: update_flat(module, binary, installed, method, no_dependencies) if state in ("present", "latest") and not_installed: - install_flat(module, binary, remote, not_installed, method, no_dependencies) + install_flat(module, binary, remote, not_installed, method, no_dependencies, from_url) module.exit_json(**result) diff --git a/tests/integration/targets/flatpak/tasks/check_mode.yml b/tests/integration/targets/flatpak/tasks/check_mode.yml index b4538200ff..1d80716c62 100644 --- a/tests/integration/targets/flatpak/tasks/check_mode.yml +++ b/tests/integration/targets/flatpak/tasks/check_mode.yml @@ -84,6 +84,40 @@ state=latest an absent flatpak a second time shall still mark module execution as changed in check mode +# state=present with from_url on absent flatpak + +- name: Test addition of absent flatpak with from_url (check mode) + flatpak: + name: com.dummy.App1 + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + no_dependencies: true + register: from_url_addition_result + check_mode: true + +- name: Verify addition of absent flatpak with from_url test result (check mode) + assert: + that: + - from_url_addition_result is changed + msg: "Adding an absent flatpak with from_url shall mark module execution as changed" + +- name: Test non-existent idempotency of addition of absent flatpak with from_url (check mode) + flatpak: + name: com.dummy.App1 + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + no_dependencies: true + register: double_from_url_addition_result + check_mode: true + +- name: Verify non-existent idempotency of addition of absent flatpak with from_url test result (check mode) + assert: + that: + - double_from_url_addition_result is changed + msg: | + Adding an absent flatpak with from_url a second time shall still mark module execution + as changed in check mode + # state=present with url on absent flatpak - name: Test addition of absent flatpak with url (check mode) @@ -231,6 +265,23 @@ - latest_present_result is changed msg: "state=latest an present flatpak shall mark module execution as changed" +# state=present with from_url on present flatpak + +- name: Test addition with from_url of present flatpak (check mode) + flatpak: + name: com.dummy.App2 + from_url: http://127.0.0.1:8000/repo/com.dummy.App2.flatpakref + state: present + no_dependencies: true + register: from_url_addition_present_result + check_mode: true + +- name: Verify addition with from_url of present flatpak test result (check mode) + assert: + that: + - from_url_addition_present_result is not changed + msg: "Adding a present flatpak with from_url shall mark module execution as not changed" + # state=present with url on present flatpak - name: Test addition with url of present flatpak (check mode) diff --git a/tests/integration/targets/flatpak/tasks/test.yml b/tests/integration/targets/flatpak/tasks/test.yml index 1c580b6fbf..15d136e8b8 100644 --- a/tests/integration/targets/flatpak/tasks/test.yml +++ b/tests/integration/targets/flatpak/tasks/test.yml @@ -230,6 +230,97 @@ method: "{{ method }}" no_dependencies: true +# state=present with from_url + +- name: Test addition with from_url - {{ method }} + flatpak: + name: com.dummy.App1 + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + method: "{{ method }}" + no_dependencies: true + register: from_url_addition_result + +- name: Verify addition with from_url test result - {{ method }} + assert: + that: + - from_url_addition_result is changed + msg: "state=present with from_url shall add flatpak when absent" + +- name: Test idempotency of addition with from_url - {{ method }} + flatpak: + name: com.dummy.App1 + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + method: "{{ method }}" + no_dependencies: true + register: double_from_url_addition_result + +- name: Verify idempotency of addition with from_url test result - {{ method }} + assert: + that: + - double_from_url_addition_result is not changed + msg: "state=present with from_url shall not do anything when flatpak is already present" + +- name: Cleanup after state=present with from_url test - {{ method }} + flatpak: + name: com.dummy.App1 + state: absent + method: "{{ method }}" + no_dependencies: true + +# from_url validation failures + +- name: Test from_url with URL in name fails - {{ method }} + flatpak: + name: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + method: "{{ method }}" + register: from_url_url_name_result + ignore_errors: true + +- name: Verify from_url with URL in name fails - {{ method }} + assert: + that: + - from_url_url_name_result is failed + - from_url_url_name_result is not changed + msg: "from_url and URL in name shall fail" + +- name: Test from_url with multiple names fails - {{ method }} + flatpak: + name: + - com.dummy.App1 + - com.dummy.App2 + from_url: http://127.0.0.1:8000/repo/com.dummy.App1.flatpakref + state: present + method: "{{ method }}" + register: from_url_multi_name_result + ignore_errors: true + +- name: Verify from_url with multiple names fails - {{ method }} + assert: + that: + - from_url_multi_name_result is failed + - from_url_multi_name_result is not changed + msg: "from_url with multiple names shall fail" + +- name: Test from_url with non-http URL fails - {{ method }} + flatpak: + name: com.dummy.App1 + from_url: file:///tmp/flatpak/repo/com.dummy.App1.flatpakref + state: present + method: "{{ method }}" + register: from_url_non_http_result + ignore_errors: true + +- name: Verify from_url with non-http URL fails - {{ method }} + assert: + that: + - from_url_non_http_result is failed + - from_url_non_http_result is not changed + msg: "from_url with non-http URL shall fail" + # state=present with list of packages - name: Test addition with list - {{ method }}