blob: 62a8f4a94e8369f2fdf8c0fa3cb8946a2e8201bc [file] [log] [blame]
Andrew Geissler384f1902020-04-02 13:11:28 +00001#!/usr/bin/env python3
Xo Wang49abd7e2017-03-17 18:15:10 -07002"""Loads a "target" YAML file and overwrites its values with values from
3"override" YAML files.
4
5Override files are processed in the order given.
6
7Usage:
Gaurav Gandhid08d1062022-02-18 21:24:19 +00008 merge_settings.py <target yaml> [override/remove yamls]
Xo Wang49abd7e2017-03-17 18:15:10 -07009"""
10import sys
11import yaml
12import copy
13
Gaurav Gandhid08d1062022-02-18 21:24:19 +000014def dict_merge(target, source, remove):
Xo Wang49abd7e2017-03-17 18:15:10 -070015 """Deep merge for dicts.
16
Gaurav Gandhid08d1062022-02-18 21:24:19 +000017 Works like dict.update() that recursively updates/removes any dict values
18 present in both parameters.
Xo Wang49abd7e2017-03-17 18:15:10 -070019
20 Args:
21 target (dict): Values to be overwritten by corresponding values from
22 `source`.
23 source (dict): Overriding values. Not changed by call.
Gaurav Gandhid08d1062022-02-18 21:24:19 +000024 remove (bool): If this is true then it removes the entry provided in
25 'source' along with all entries under it from 'target'. Otherwise
26 it overrides the values from 'source'
Xo Wang49abd7e2017-03-17 18:15:10 -070027
28 Returns:
Gaurav Gandhid08d1062022-02-18 21:24:19 +000029 `target` with values overwritten/removed from those in `source` at any
30 and all levels of nested dicts.
Xo Wang49abd7e2017-03-17 18:15:10 -070031 """
32 if not isinstance(source, dict):
33 return source
Andrew Geissler384f1902020-04-02 13:11:28 +000034 for k, v in source.items():
Xo Wang49abd7e2017-03-17 18:15:10 -070035 if k in target and isinstance(target[k], dict):
Gaurav Gandhid08d1062022-02-18 21:24:19 +000036 dict_merge(target[k], v, remove)
Xo Wang49abd7e2017-03-17 18:15:10 -070037 else:
Allen.Wang4818d0e2022-03-07 17:05:21 +080038 if remove is True and k in target:
Gaurav Gandhid08d1062022-02-18 21:24:19 +000039 target.pop(k)
40 else:
41 target[k] = copy.deepcopy(v)
Xo Wang49abd7e2017-03-17 18:15:10 -070042 return target
43
44if len(sys.argv) < 2:
45 sys.exit('Argument required: target yaml')
46
47if len(sys.argv) == 2:
48 # No overrides to handle
49 sys.exit()
50
51target_filename = sys.argv[1]
52with open(target_filename) as target_file:
53 data = yaml.safe_load(target_file)
54 print('Loaded target YAML file ' + target_filename)
55
56for override_filename in sys.argv[2:]:
Gaurav Gandhid08d1062022-02-18 21:24:19 +000057 if override_filename.endswith('.override.yml'):
58 with open(override_filename) as override_file:
59 override = yaml.safe_load(override_file)
60 dict_merge(data, override, False)
61 print('Merged override YAML file ' + override_filename)
62 elif override_filename.endswith('.remove.yml'):
63 with open(override_filename) as override_file:
64 override = yaml.safe_load(override_file)
65 dict_merge(data, override, True)
66 print('Removed data from source YAML file' + override_filename)
Xo Wang49abd7e2017-03-17 18:15:10 -070067
68with open(target_filename, 'w') as target_file:
69 yaml.dump(data, target_file)
70 print('Wrote merged target YAML file ' + target_filename)