--- - name: Get list of upgradable packages (for reporting) ansible.builtin.shell: | apt list --upgradable 2>/dev/null | grep -v "WARNING" | tail -n +2 | wc -l args: executable: /bin/bash register: maintenance_upgradable_count changed_when: false - name: Display packages to be upgraded ansible.builtin.debug: msg: | 📦 Package Upgrade Summary: - Total packages to upgrade: {{ maintenance_upgradable_count.stdout | default('0') }} - Upgrade type: {{ maintenance_upgrade_type }} - Max retries: {{ maintenance_apt_retries }} - Retry delay: {{ maintenance_apt_retry_delay }}s when: ansible_debug_output | default(false) | bool - name: Show upgradable packages (first 10) ansible.builtin.shell: | apt list --upgradable 2>/dev/null | grep -v "WARNING" | head -10 args: executable: /bin/bash register: maintenance_upgradable_packages_preview changed_when: false when: maintenance_upgradable_count.stdout | int > 0 - name: Display upgradable packages ansible.builtin.debug: msg: | 📋 Packages to upgrade: {{ maintenance_upgradable_packages_preview.stdout_lines | join('\n') }} when: - maintenance_upgradable_count.stdout | int > 0 - ansible_debug_output | default(false) | bool - maintenance_upgradable_packages_preview.stdout_lines is defined - name: Update apt cache with retries ansible.builtin.apt: update_cache: "{{ maintenance_update_cache }}" cache_valid_time: "{{ maintenance_cache_valid_time }}" retries: "{{ maintenance_apt_retries }}" delay: "{{ maintenance_cache_retry_delay }}" register: maintenance_apt_update_result until: maintenance_apt_update_result is succeeded when: maintenance_update_cache | bool - name: Fix broken packages if any ansible.builtin.command: cmd: apt-get --fix-broken install -y # noqa command-instead-of-module become: true changed_when: false failed_when: false when: maintenance_fix_broken | bool - name: Upgrade packages with retries and verbose output ansible.builtin.apt: upgrade: "{{ maintenance_upgrade_type }}" force_apt_get: true update_cache: false # Already updated above environment: DEBIAN_FRONTEND: noninteractive APT_LISTCHANGES_FRONTEND: none retries: "{{ maintenance_apt_retries }}" delay: "{{ maintenance_apt_retry_delay }}" register: maintenance_apt_upgrade_result until: maintenance_apt_upgrade_result is succeeded ignore_errors: true async: "{{ maintenance_bulk_upgrade_timeout }}" poll: "{{ maintenance_bulk_poll_interval }}" no_log: "{{ not (ansible_debug_output | default(false) | bool) }}" when: maintenance_upgrade_packages | bool - name: Fallback to individual package upgrades if full upgrade failed when: - maintenance_apt_upgrade_result is failed - maintenance_individual_upgrade_fallback | bool block: - name: Display fallback message ansible.builtin.debug: msg: | ⚠️ Bulk upgrade failed, trying individual package upgrades... This may take longer but is more reliable for problematic packages. - name: Get list of upgradable packages for individual upgrade ansible.builtin.shell: | apt list --upgradable 2>/dev/null | grep -v "WARNING" | tail -n +2 | cut -d'/' -f1 args: executable: /bin/bash register: maintenance_upgradable_packages_fallback changed_when: false - name: Display packages for individual upgrade ansible.builtin.debug: msg: | 🔄 Attempting individual upgrade for {{ maintenance_upgradable_packages_fallback.stdout_lines | length }} packages: {{ maintenance_upgradable_packages_fallback.stdout_lines | join(', ') }} when: maintenance_upgradable_packages_fallback.stdout_lines | length > 0 - name: Upgrade packages individually with progress ansible.builtin.apt: name: "{{ item }}" state: latest # noqa package-latest - This is intentional for maintenance upgrades force_apt_get: true environment: DEBIAN_FRONTEND: noninteractive APT_LISTCHANGES_FRONTEND: none loop: "{{ maintenance_upgradable_packages_fallback.stdout_lines | default([]) }}" loop_control: label: "📦 Upgrading {{ item }} ({{ ansible_loop.index }}/{{ ansible_loop.length }})" retries: 2 delay: 10 ignore_errors: true async: "{{ maintenance_individual_timeout }}" poll: "{{ maintenance_individual_poll_interval }}" no_log: "{{ not (ansible_debug_output | default(false) | bool) }}" register: maintenance_individual_upgrade_results when: maintenance_upgradable_packages_fallback.stdout_lines | length > 0 - name: Summary of individual upgrades ansible.builtin.debug: msg: | 📊 Individual Upgrade Results: - Total attempted: {{ maintenance_individual_upgrade_results.results | length | default(0) }} - Successful: {{ maintenance_individual_upgrade_results.results | selectattr('changed', 'equalto', true) | list | length | default(0) }} - Failed: {{ maintenance_individual_upgrade_results.results | selectattr('failed', 'equalto', true) | list | length | default(0) }} when: - maintenance_individual_upgrade_results is defined - maintenance_individual_upgrade_results.results is defined - name: Autoremove unused packages ansible.builtin.apt: autoremove: true purge: true retries: 2 delay: 5 when: maintenance_autoremove | bool - name: Clean apt cache ansible.builtin.apt: autoclean: true when: maintenance_autoclean | bool - name: Check if reboot is required ansible.builtin.stat: path: /var/run/reboot-required register: maintenance_reboot_required when: maintenance_check_reboot | bool - name: Display maintenance summary ansible.builtin.debug: msg: | Maintenance Summary: - Cache update: {{ 'Completed' if maintenance_apt_update_result is succeeded else 'Skipped/Failed' }} - Package upgrade: {{ 'Completed' if maintenance_apt_upgrade_result is succeeded else 'Failed (fallback may have run)' }} - Reboot required: {{ 'Yes' if (maintenance_reboot_required.stat.exists | default(false)) else 'No' }} when: ansible_debug_output | default(false) | bool - name: Reboot if required ansible.builtin.reboot: msg: "Reboot triggered by Ansible after system maintenance." reboot_timeout: "{{ maintenance_reboot_timeout }}" pre_reboot_delay: "{{ maintenance_pre_reboot_delay }}" when: - maintenance_check_reboot | bool - maintenance_allow_reboot | bool - maintenance_reboot_required.stat.exists | default(false) - not (skip_reboot | default(false) | bool)