diff --git a/bin/compose_format b/bin/compose_format index 093638e..84e4416 100755 --- a/bin/compose_format +++ b/bin/compose_format @@ -14,6 +14,9 @@ if __name__ == '__main__': parser.add_argument( '--ignore_changes', action='store_const', const=False, help='ignore changes for return code', default=True) + parser.add_argument( + '--non_strict', action='store_const', const=True, + help='if this is provided, unknown keys errors are ignored', default=False) parser.add_argument('files', nargs=argparse.REMAINDER) args = parser.parse_args() formatter = ComposeFormat() @@ -22,14 +25,14 @@ if __name__ == '__main__': assert args.replace is False, 'replace makes no sense when reading from stdin' data = sys.stdin.read() - formatted = formatter.format_string(data) + formatted = formatter.format_string(data, strict=not args.non_strict) print(formatted) if not args.ignore_changes: if data != formatted: sys.exit(1) for path in args.files: - if not formatter.format(path, args.replace): + if not formatter.format(path, replace=args.replace, strict=not args.non_strict): if args.replace: print('path {} has been changed'.format(path)) if not args.ignore_changes: diff --git a/compose_format/__init__.py b/compose_format/__init__.py index 4f2451a..59ef5f2 100755 --- a/compose_format/__init__.py +++ b/compose_format/__init__.py @@ -7,10 +7,24 @@ class ComposeFormat: TOPLEVEL_ORDER = ['version', 'services', 'volumes', 'networks'] SERVICE_ORDER = [ - 'image', 'command', 'links', - 'volumes_from', 'volumes', 'expose', 'ports', - 'extra_hosts', 'restart', 'ulimits', 'tty'] - BUILD_ORDER = ['context', 'dockerfile'] + 'image', 'command', 'entrypoint', 'container_name', + 'links', 'volumes_from', 'volumes', 'volume_driver', + 'build', + 'expose', 'ports', + 'net', 'network_mode', 'networks', + 'labels', + 'devices', + 'read_only', + 'env_file', 'environment', + 'cpu_shares', 'cpu_quota', 'cpuset', 'domainname', 'hostname', 'ipc', + 'mac_address', 'mem_limit', 'memswap_limit', 'privileged', + 'depends_on', 'extends', 'external_links', + 'stdin_open', 'user', 'working_dir', + 'extra_hosts', 'restart', 'ulimits', 'tty', 'dns', 'dns_search', 'pid', + 'security_opt', 'cap_add', 'cap_drop', 'cgroup_parent', 'logging', 'log_driver', 'log_opt', + 'stopsignal', + ] + BUILD_ORDER = ['context', 'dockerfile', 'args'] ORDERS = { 'version': TOPLEVEL_ORDER, @@ -21,11 +35,11 @@ class ComposeFormat: def __init__(self): pass - def format(self, path, replace=False): + def format(self, path, replace=False, strict=True): with open(path, 'r') as file: data = file.read() original = data - formatted = self.format_string(data, replace=replace) + formatted = self.format_string(data, replace=replace, strict=strict) if replace: with open(path, 'w') as file: @@ -34,8 +48,8 @@ def format(self, path, replace=False): print(formatted) return original == formatted - def format_string(self, data, replace=False): - data = self.reorder(load(data)) + def format_string(self, data, replace=False, strict=True): + data = self.reorder(load(data), strict=strict) def is_legacy_version(data): if 'version' not in data: @@ -48,7 +62,7 @@ def is_legacy_version(data): return formatted.strip() + '\n' @staticmethod - def reorder(data): + def reorder(data, strict=True): if type(data) is dict or type(data) is OrderedDict: for key in ComposeFormat.ORDERS.keys(): if key not in data.keys(): @@ -57,19 +71,26 @@ def reorder(data): def order(item): key, _ = item - assert key in current_order, 'key: {0} not known'.format(key) + if strict: + assert key in current_order, 'key: {0} not known'.format(key) if key in current_order: return current_order.index(key) - return len(current_order) + return len(current_order) + ComposeFormat.name_to_order(key) - result = {key: ComposeFormat.reorder(value) for key, value in data.items()} + result = {key: ComposeFormat.reorder(value, strict=strict) for key, value in data.items()} result = OrderedDict(sorted(result.items(), key=order)) return result - return {key: ComposeFormat.reorder(value) for key, value in data.items()} + return {key: ComposeFormat.reorder(value, strict=strict) for key, value in data.items()} if type(data) is list: - return sorted([ComposeFormat.reorder(item) for item in data]) + return sorted([ComposeFormat.reorder(item, strict=strict) for item in data]) if len(str(data)) >= 1 and str(data)[0].isdigit(): return '\'{}\''.format(data) return data + + @staticmethod + def name_to_order(value): + from functools import reduce + + return reduce(lambda left, right: (left * 256 + right), (ord(char) for char in value)) diff --git a/features/format.feature b/features/format.feature index 6cc80b9..244d123 100644 --- a/features/format.feature +++ b/features/format.feature @@ -169,3 +169,22 @@ Feature: Format ports: - '10000' """ + + Scenario: Alphabetic Order for unknown keys (--non_strict) + Given a file named "compose.yml" with: + """ + foo: + image: bar + aaa: unknown + ccc: unknown + bbb: unknown + """ + When I run `bin/compose_format --non_strict compose.yml` + Then it should pass with exactly: + """ + foo: + image: bar + aaa: unknown + bbb: unknown + ccc: unknown + """ diff --git a/setup.py b/setup.py index 96bfed1..cf2c5d3 100644 --- a/setup.py +++ b/setup.py @@ -7,7 +7,7 @@ def readme(): setup( name='compose_format', - version='0.1.4', + version='0.1.5', description='format docker-compose files', long_description=readme(), url='http://github.com/funkwerk/compose_format',