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.

Co-authored-by: Felix Fontein <felix@fontein.de>

* 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

Co-authored-by: Felix Fontein <felix@fontein.de>

---------

Co-authored-by: Felix Fontein <felix@fontein.de>
This commit is contained in:
Allen Smith 2025-08-28 14:09:15 -06:00 committed by GitHub
commit b1c75339c0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 57 additions and 7 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- openbsd_pkg - add ``autoremove`` parameter to remove unused dependencies (https://github.com/ansible-collections/community.general/pull/10705).

View file

@ -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)