Blob Blame History Raw
#!/usr/bin/python3
"""
Script for sorting YAML files
Copyright (C) 2023 Georg Pfuetzenreuter <mail+opensuse@georg-pfuetzenreuter.net>

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import sys

import yaml


def _fail(msg):
    print(f'{msg}, bailing out.')
    sys.exit(1)

args = sys.argv
indent = False
if '--indent' in args:
  indent = True
  args.remove('--indent')
  # https://github.com/yaml/pyyaml/issues/234#issuecomment-765894586
  class IndentedDumper(yaml.Dumper):
      def increase_indent(self, flow=False, *args, **kwargs): # noqa FBT002 # boolean is simple enough here
          return super().increase_indent(flow=flow, indentless=False)

sort_lists = False
if '--sort-lists' in args:
  sort_lists = True
  args.remove('--sort-lists')

if len(args) < 2 or not args[1].endswith(('.yaml', '.yml')):
    _fail('Cannot operate without being passed a YAML file')

files = sys.argv[1:]
out = {}

for file in files:
    try:
        with open(file) as fh:
            data = yaml.safe_load(fh)
    except FileNotFoundError:
        _fail(f'Passed file {file} does not exist')

    if data is None:
        _fail(f'Invalid data in file {file}')

    out.update({file: data})


if sort_lists:
  out_sort = out.copy()

  # https://stackoverflow.com/a/56305689
  def deepsort(obj):
    if isinstance(obj, list):
      return sorted(deepsort(item) for item in obj)
    if isinstance(obj, dict):
      return {key: deepsort(obj[key]) for key in sorted(obj)}
    return obj

  for file, data in out.items():
    out_sort.update({file: {}})
    for key, value in data.items():
      out_sort[file].update({key: deepsort(value)})

  out = out_sort


for file, data in out.items():
    with open(file, 'w') as fh:
        if indent is True:
            yaml.dump(data, fh, explicit_start=True, Dumper=IndentedDumper)
        else:
            yaml.safe_dump(data, fh, explicit_start=True)