Search code examples
sortingansiblejinja2

How to sort complex version numbers in Ansible


I'm building an Ansible playbook in which I want to retrieve the latest version of a software. For this I used "sort" filter in Ansible. That, however, becomes a bit harder, when using version numbers, that are more complex and are not really numbers, e.g. 0.2.1, 0.10.1.

This is what I'm doing right now:

- name: Set version to compare
  set_fact:
    versions:
      - "0.1.0"
      - "0.1.5"
      - "0.11.11"
      - "0.9.11"
      - "0.9.3"
      - "0.10.2"
      - "0.6.1"
      - "0.6.0"
      - "0.11.0"
      - "0.6.5"

- name: Sorted list
  debug:
    msg: "{{ versions | sort }}"

- name: Show the latest element
  debug:
    msg: "{{ versions | sort | last }}"

The playbook above works, as long as version numbers stay underneath the number 10 (e.g. 0.9.3, but not 0.10.2).

To show the issue:

TASK [Set version to compare] ***************************************************************************************************************
ok: [localhost]

TASK [Sorted list] **************************************************************************************************************************
ok: [localhost] => {
    "msg": [
        "0.1.0",
        "0.1.5",
        "0.10.2",
        "0.11.0",
        "0.11.11",
        "0.6.0",
        "0.6.1",
        "0.6.5",
        "0.9.11",
        "0.9.3"
    ]
}

TASK [Show the latest element] **************************************************************************************************************
ok: [localhost] => {
    "msg": "0.9.3"
}

In this example the value the desired value is 0.11.11

Does anyone know a good way to sort complex version numbers in Ansible? Any help would be appreciated. Thanks.


Solution

  • An option would be to write a filter plugin. For example,

    shell> cat filter_plugins/sort_versions.py
    from distutils.version import LooseVersion
    
    
    def sort_versions(value):
        return sorted(value, key=LooseVersion)
    
    
    class FilterModule(object):
    
        def filters(self):
            return {
                'sort_versions': sort_versions,
            }
    

    Then the task below

        - debug:
            msg: "{{ versions|sort_versions }}"
    

    gives

      msg:
      - 0.1.0
      - 0.1.5
      - 0.6.0
      - 0.6.1
      - 0.6.5
      - 0.9.3
      - 0.9.11
      - 0.10.2
      - 0.11.0
      - 0.11.11
    

    You don't have to write the filter if you can install the collection community.general. Use the filter community.general.version_sort. Then, the task below gives the same result

        - debug:
            msg: "{{ versions|community.general.version_sort }}"
    

    Example of a complete playbook for testing

    - hosts: all
    
      vars:
    
        versions:
          - '0.1.0'
          - '0.1.5'
          - '0.11.11'
          - '0.9.11'
          - '0.9.3'
          - '0.10.2'
          - '0.6.1'
          - '0.6.0'
          - '0.11.0'
          - '0.6.5'
    
      tasks:
    
        - debug:
            msg: "{{ versions|sort_versions }}"
    
        - debug:
            msg: "{{ versions|community.general.version_sort }}"