From cf1b02c6b998432905ae7a546d5f6a0093d01430 Mon Sep 17 00:00:00 2001 From: "patchback[bot]" <45432694+patchback[bot]@users.noreply.github.com> Date: Thu, 28 Aug 2025 22:18:07 +0200 Subject: [PATCH] [PR #10705/b1c75339 backport][stable-11] openbsd_pkg: add support for removing unused dependencies (#10758) openbsd_pkg: add support for removing unused dependencies (#10705) * openbsd_pkg: add support for removing unused dependencies Add new state 'rm_unused_deps' that uses 'pkg_delete -a' to remove packages that are no longer required by any other packages. Features: - Requires name='*' to avoid accidental usage - Supports check mode, diff mode, clean and quick flags - Follows existing module patterns for error handling - Integrates with existing package list comparison for change detection * Update the PR number in the frgment link * Fix the changelog fragment name to include the PR # * Force non-interactive mode like most of the other modes * Fix PEP8 E302: add missing blank line before function definition * Ensure that no matter what, if the package list unchanged then there was no change Also removed some unused vars from the original code. * Standardize names in the PR * Swap over from a new state to implementing an autoremove option Added code to handle the case where you git a name or list of names as pkg_delete will correctly filter what it autoremove by the names * Update the fragment to match the new code * typo in EXAMPLES * Fix up a yamllint complaint. I do note the following: ``` $ ansible-lint tests/test_openbsd_pkg.yml Passed: 0 failure(s), 0 warning(s) on 1 files. Last profile that met the validation criteria was 'production'. ``` Although that could be due to local config * While here add realistic examples of packages that might be autoinstalled * Clean up docs. * Autoremove is an option, work like the other package managers * Update changelog for openbsd_pkg autoremove parameter Clarified the behavior of the `autoremove` parameter to specify it removes autoinstalled packages. Removed flowery text that isn't needed. * Cut the rest of the cruft out of the changelog fragment Make it obvious how '*' can be used as a 'name:' Be more pythonic in the package list comparison. * Update changelogs/fragments/10705-openbsd-pkg-remove-unused.yml --------- (cherry picked from commit b1c75339c08d3a2839a84c1725b0bc40a1da80e3) Co-authored-by: Allen Smith Co-authored-by: Felix Fontein --- .../10705-openbsd-pkg-remove-unused.yml | 2 + plugins/modules/openbsd_pkg.py | 62 ++++++++++++++++--- 2 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 changelogs/fragments/10705-openbsd-pkg-remove-unused.yml diff --git a/changelogs/fragments/10705-openbsd-pkg-remove-unused.yml b/changelogs/fragments/10705-openbsd-pkg-remove-unused.yml new file mode 100644 index 0000000000..2ceb1352b4 --- /dev/null +++ b/changelogs/fragments/10705-openbsd-pkg-remove-unused.yml @@ -0,0 +1,2 @@ +minor_changes: + - openbsd_pkg - add ``autoremove`` parameter to remove unused dependencies (https://github.com/ansible-collections/community.general/pull/10705). diff --git a/plugins/modules/openbsd_pkg.py b/plugins/modules/openbsd_pkg.py index e81fce3018..66779a9f93 100644 --- a/plugins/modules/openbsd_pkg.py +++ b/plugins/modules/openbsd_pkg.py @@ -72,6 +72,12 @@ options: - Replace or delete packages quickly; do not bother with checksums before removing normal files. type: bool default: false + autoremove: + description: + - Calls C(pkg_delete -a) to remove automatically installed packages which are no longer needed. + type: bool + default: false + version_added: 11.3.0 notes: - When used with a C(loop:) each package is processed individually, it is much more efficient to pass the list directly to the O(name) option. @@ -130,6 +136,16 @@ EXAMPLES = r""" name: qt5 quick: true state: absent + +- name: Install packages, remove unused dependencies + community.general.openbsd_pkg: + name: ["tree", "mtr"] + autoremove: true + +- name: Remove all unused dependencies + community.general.openbsd_pkg: + name: '*' + autoremove: true """ import os @@ -383,6 +399,30 @@ def package_absent(names, pkg_spec, module): pkg_spec[name]['changed'] = False +# Function used to remove unused dependencies. +def package_rm_unused_deps(pkg_spec, module): + rm_unused_deps_cmd = 'pkg_delete -Ia' + + if module.check_mode: + rm_unused_deps_cmd += 'n' + + if module.params['clean']: + rm_unused_deps_cmd += 'c' + + if module.params['quick']: + rm_unused_deps_cmd += 'q' + + # If we run the commands, we set changed to true to let + # the package list change detection code do the actual work. + + # Create a minimal pkg_spec entry for '*' to store return values. + pkg_spec['*'] = {} + + # Attempt to remove unused dependencies. + pkg_spec['*']['rc'], pkg_spec['*']['stdout'], pkg_spec['*']['stderr'] = execute_command(rm_unused_deps_cmd, module) + pkg_spec['*']['changed'] = True + + # Function used to parse the package name based on packages-specs(7). # The general name structure is "stem-version[-flavors]". # @@ -567,6 +607,7 @@ def main(): ports_dir=dict(type='path', default='/usr/ports'), quick=dict(type='bool', default=False), clean=dict(type='bool', default=False), + autoremove=dict(type='bool', default=False), ), mutually_exclusive=[['snapshot', 'build']], supports_check_mode=True @@ -577,9 +618,6 @@ def main(): build = module.params['build'] ports_dir = module.params['ports_dir'] - rc = 0 - stdout = '' - stderr = '' result = {} result['name'] = name result['state'] = state @@ -611,11 +649,16 @@ def main(): asterisk_name = True if asterisk_name: - if state != 'latest': - module.fail_json(msg="the package name '*' is only valid when using state=latest") - else: + if state != 'latest' and not module.params['autoremove']: + module.fail_json(msg="the package name '*' is only valid when using state=latest or autoremove=true") + + if state == 'latest': # Perform an upgrade of all installed packages. upgrade_packages(pkg_spec, module) + + if module.params['autoremove']: + # Remove unused dependencies. + package_rm_unused_deps(pkg_spec, module) else: # Parse package names and put results in the pkg_spec dictionary. parse_package_name(name, pkg_spec, module) @@ -637,6 +680,10 @@ def main(): elif state == 'latest': package_latest(name, pkg_spec, module) + # Handle autoremove if requested for non-asterisk packages + if module.params['autoremove']: + package_rm_unused_deps(pkg_spec, module) + # The combined changed status for all requested packages. If anything # is changed this is set to True. combined_changed = False @@ -675,9 +722,10 @@ def main(): result['changed'] = combined_changed - if result['changed'] and not module.check_mode: + if not module.check_mode: new_package_list = get_all_installed(module) result['diff'] = dict(before=original_package_list, after=new_package_list) + result['changed'] = (result['diff']['before'] != result['diff']['after']) module.exit_json(**result)