azure-cli-diff-tool 0.0.4__tar.gz → 0.0.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/PKG-INFO +10 -1
  2. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/__init__.py +6 -4
  3. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/_const.py +37 -6
  4. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/meta_change.py +122 -28
  5. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/meta_change_detect.py +184 -71
  6. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/utils.py +49 -0
  7. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool.egg-info/PKG-INFO +10 -1
  8. azure-cli-diff-tool-0.0.6/tests/test_break_change.py +168 -0
  9. azure-cli-diff-tool-0.0.4/tests/test_break_change.py +0 -88
  10. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/README.rst +0 -0
  11. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/data/blob_config.ini +0 -0
  12. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool/data/meta_change_whitelist.txt +0 -0
  13. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool.egg-info/SOURCES.txt +0 -0
  14. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool.egg-info/dependency_links.txt +0 -0
  15. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool.egg-info/requires.txt +0 -0
  16. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/azure_cli_diff_tool.egg-info/top_level.txt +0 -0
  17. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/setup.cfg +0 -0
  18. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/setup.py +0 -0
  19. {azure-cli-diff-tool-0.0.4 → azure-cli-diff-tool-0.0.6}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-cli-diff-tool
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: A tool for cli metadata management
5
5
  Author: Microsoft Corporation
6
6
  Author-email: azpycli@microsoft.com
@@ -98,6 +98,15 @@ License
98
98
 
99
99
  Release History
100
100
  ===============
101
+ 0.0.6
102
+ ++++++
103
+ * Add diff support for deprecate_info in subgroup, cmd, parameters and options
104
+
105
+ 0.0.5
106
+ ++++++
107
+ * Add `DiffLevel` to meta comparison
108
+ * Downgrade change level according to warn list
109
+
101
110
  0.0.4
102
111
  ++++++
103
112
  * Add meta change parameter update warn list
@@ -16,9 +16,9 @@ from deepdiff import DeepDiff
16
16
  from .meta_change_detect import MetaChangeDetect
17
17
  from .utils import get_blob_config, load_blob_config_file, get_target_version_modules, get_target_version_module, \
18
18
  extract_module_name_from_meta_file, export_meta_changes_to_csv, export_meta_changes_to_json, \
19
- export_meta_changes_to_dict
19
+ export_meta_changes_to_dict, expand_deprecate_obj
20
20
 
21
- __VERSION__ = '0.0.4'
21
+ __VERSION__ = '0.0.6'
22
22
 
23
23
  logger = logging.getLogger(__name__)
24
24
 
@@ -61,7 +61,8 @@ def meta_diff(base_meta_file, diff_meta_file, only_break=False, output_type="tex
61
61
  if command_tree_after.get("compat_version", None) \
62
62
  and check_meta_tool_compatibility(command_tree_after["compat_version"]):
63
63
  raise Exception("Please update your azure cli diff tool")
64
-
64
+ expand_deprecate_obj(command_tree_before)
65
+ expand_deprecate_obj(command_tree_after)
65
66
  diff = DeepDiff(command_tree_before, command_tree_after)
66
67
  if not diff:
67
68
  print(f"No meta diffs from {diff_meta_file} to {base_meta_file}")
@@ -116,7 +117,8 @@ def version_diff(base_version, diff_version, only_break=False, version_diff_file
116
117
  if command_tree_after.get("compat_version", None) \
117
118
  and check_meta_tool_compatibility(command_tree_after["compat_version"]):
118
119
  raise Exception("Please update your azure cli diff tool")
119
-
120
+ expand_deprecate_obj(command_tree_before)
121
+ expand_deprecate_obj(command_tree_after)
120
122
  diff = DeepDiff(command_tree_before, command_tree_after)
121
123
  if not diff:
122
124
  print(f"No meta diffs from version: {diff_version}/{base_meta_file} for module: {module_name}")
@@ -17,22 +17,47 @@ DOWNLOAD_THREADS = 30
17
17
  BREAKING_CHANE_RULE_LINK_URL_PREFIX = "https://github.com/Azure/azure-cli/blob/dev/doc/breaking_change_rules/"
18
18
  BREAKING_CHANE_RULE_LINK_URL_SUFFIX = ".md"
19
19
 
20
+ CMD_REMOVE_SUFFIX_WARN_LIST = ["wait"]
21
+
22
+ SUBGROUP_PROPERTY_REMOVE_BREAK_LIST = []
23
+ SUBGROUP_PROPERTY_REMOVE_WARN_LIST = ["deprecate_info_redirect"]
24
+
25
+ SUBGROUP_PROPERTY_ADD_BREAK_LIST = []
26
+ SUBGROUP_PROPERTY_ADD_WARN_LIST = ["deprecate_info_hide", "deprecate_info_expiration"]
27
+
28
+ SUBGROUP_PROPERTY_UPDATE_BREAK_LIST = []
29
+ SUBGROUP_PROPERTY_UPDATE_WARN_LIST = ["deprecate_info_expiration"]
30
+
31
+ SUBGROUP_PROPERTY_IGNORED_LIST = []
32
+
20
33
  CMD_PROPERTY_REMOVE_BREAK_LIST = []
34
+ CMD_PROPERTY_REMOVE_WARN_LIST = ["deprecate_info_redirect"]
35
+
21
36
  CMD_PROPERTY_ADD_BREAK_LIST = ["confirmation"]
37
+ CMD_PROPERTY_ADD_WARN_LIST = ["deprecate_info_hide", "deprecate_info_expiration"]
38
+
22
39
  CMD_PROPERTY_UPDATE_BREAK_LIST = []
40
+ CMD_PROPERTY_UPDATE_WARN_LIST = ["deprecate_info_expiration", "deprecate_info_redirect"]
41
+
23
42
  CMD_PROPERTY_IGNORED_LIST = ["is_aaz", "supports_no_wait"]
24
43
 
25
- PARA_PROPERTY_REMOVE_BREAK_LIST = ["options", "id_part", "nargs"]
26
- PARA_PROPERTY_ADD_BREAK_LIST = ["required", "choices"]
27
- PARA_PROPERTY_UPDATE_BREAK_LIST = ["default", "aaz_default", "type", "aaz_type"]
28
- PARA_PROPERTY_UPDATE_WARN_LIST = ["type", "aaz_type"]
44
+ PARA_PROPERTY_REMOVE_BREAK_LIST = ["options"]
45
+ PARA_PROPERTY_REMOVE_WARN_LIST = ["id_part", "nargs", "deprecate_info_redirect"]
46
+
47
+ PARA_PROPERTY_ADD_BREAK_LIST = ["required"]
48
+ PARA_PROPERTY_ADD_WARN_LIST = ["choices", "deprecate_info_expiration", "deprecate_info_hide", "options_deprecate_info"]
49
+
50
+ PARA_PROPERTY_UPDATE_BREAK_LIST = ["default", "aaz_default"]
51
+ PARA_PROPERTY_UPDATE_WARN_LIST = ["type", "aaz_type", "choices", "nargs",
52
+ "deprecate_info_expiration", "deprecate_info_redirect", "options_deprecate_info"]
53
+
29
54
  PARA_NAME_IGNORED_LIST = ["force_string"]
30
55
  PARA_PROPERTY_IGNORED_LIST = []
31
56
  PARA_VALUE_IGNORED_LIST = ["generic_update_set", "generic_update_add", "generic_update_remove",
32
57
  "generic_update_force_string"]
33
58
 
34
- EXPORTED_CSV_META_HEADER = ["module", "cmd_name", "rule_id", "rule_name", "is_break", "rule_link_url",
35
- "rule_message", "suggest_message"]
59
+ EXPORTED_CSV_META_HEADER = ["module", "cmd_name", "rule_id", "rule_name", "is_break", "diff_level",
60
+ "rule_link_url", "rule_message", "suggest_message"]
36
61
 
37
62
  CHANGE_RULE_MESSAGE_MAPPING = {
38
63
  "1000": "default Message",
@@ -48,6 +73,9 @@ CHANGE_RULE_MESSAGE_MAPPING = {
48
73
  "1010": "cmd `{0}` update parameter `{1}`: updated property `{2}` from `{3}` to `{4}`",
49
74
  "1011": "sub group `{0}` added",
50
75
  "1012": "sub group `{0}` removed",
76
+ "1013": "sub group `{0}` added property `{1}`",
77
+ "1014": "sub group `{0}` removed property `{1}`",
78
+ "1015": "sub group `{0}` updated property `{1}` from `{2}` to `{3}`",
51
79
  }
52
80
 
53
81
  CHANGE_SUGGEST_MESSAGE_MAPPING = {
@@ -64,5 +92,8 @@ CHANGE_SUGGEST_MESSAGE_MAPPING = {
64
92
  "1010": "please change property `{0}` from `{1}` to `{2}` for parameter `{3}` of cmd `{4}`",
65
93
  "1011": "please confirm sub group `{0}` added",
66
94
  "1012": "please confirm sub group `{0}` removed",
95
+ "1013": "please remove property `{0}` for sub group `{1}`",
96
+ "1014": "please add back property `{0}` for sub group `{1}`",
97
+ "1015": "please change property `{0}` from `{1}` to `{2}` for sub group `{3}`",
67
98
  }
68
99
 
@@ -4,17 +4,19 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- from .utils import get_change_rule_template, get_change_suggest_template
7
+ from .utils import get_change_rule_template, get_change_suggest_template, DiffLevel
8
8
  from ._const import BREAKING_CHANE_RULE_LINK_URL_PREFIX, BREAKING_CHANE_RULE_LINK_URL_SUFFIX, \
9
- CMD_PROPERTY_IGNORED_LIST, PARA_NAME_IGNORED_LIST, PARA_PROPERTY_IGNORED_LIST, PARA_VALUE_IGNORED_LIST
9
+ SUBGROUP_PROPERTY_IGNORED_LIST, CMD_PROPERTY_IGNORED_LIST, PARA_NAME_IGNORED_LIST, \
10
+ PARA_PROPERTY_IGNORED_LIST, PARA_VALUE_IGNORED_LIST
10
11
 
11
12
 
12
13
  class MetaChange:
13
14
 
14
- def __init__(self, rule_id="1000", is_break=False, rule_message="", suggest_message="",
15
+ def __init__(self, rule_id="1000", is_break=False, diff_level=DiffLevel.INFO, rule_message="", suggest_message="",
15
16
  is_ignore=False, filter_key=None):
16
17
  self.rule_id = rule_id
17
18
  self.is_break = is_break
19
+ self.diff_level = diff_level
18
20
  self.rule_message = rule_message
19
21
  self.suggest_message = suggest_message
20
22
  self.rule_link_url = BREAKING_CHANE_RULE_LINK_URL_PREFIX + self.rule_id + BREAKING_CHANE_RULE_LINK_URL_SUFFIX
@@ -22,7 +24,7 @@ class MetaChange:
22
24
  self.filter_key = filter_key
23
25
 
24
26
  def __str__(self):
25
- res = [self.rule_message]
27
+ res = [self.rule_message, "diff_level: " + str(self.diff_level.value)]
26
28
  if self.is_break:
27
29
  res.append("is_break: {0}".format(self.is_break))
28
30
  res.append(self.suggest_message)
@@ -30,57 +32,139 @@ class MetaChange:
30
32
 
31
33
 
32
34
  class SubgroupAdd(MetaChange):
33
- def __init__(self, subgroup_name, is_break=False):
35
+ def __init__(self, subgroup_name, is_break=False, diff_level=DiffLevel.INFO):
34
36
  if not subgroup_name:
35
37
  raise Exception("sub group name needed")
36
38
  self.rule_id = "1011"
37
39
  self.subgroup_name = subgroup_name
38
40
  self.is_break = is_break
41
+ self.diff_level = diff_level
39
42
  self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name)
40
43
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_name) \
41
44
  if self.is_break else ""
42
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
45
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
43
46
 
44
47
 
45
48
  class SubgroupRemove(MetaChange):
46
- def __init__(self, subgroup_name, is_break=True):
49
+ def __init__(self, subgroup_name, is_break=True, diff_level=DiffLevel.BREAK):
47
50
  if not subgroup_name:
48
51
  raise Exception("sub group name needed")
49
52
  self.rule_id = "1012"
50
53
  self.subgroup_name = subgroup_name
51
54
  self.is_break = is_break
55
+ self.diff_level = diff_level
52
56
  self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name)
53
57
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_name) \
54
58
  if self.is_break else ""
55
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
59
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
60
+
61
+
62
+ class SubgroupPropAdd(MetaChange):
63
+ def __init__(self, subgroup_name, subgroup_property, is_break=False, diff_level=DiffLevel.INFO):
64
+ if not subgroup_name or not subgroup_property:
65
+ raise Exception("sub group name needed")
66
+ self.rule_id = "1013"
67
+ self.is_ignore = False
68
+ self.subgroup_name = subgroup_name
69
+ self.subgroup_property = subgroup_property
70
+ self.is_break = is_break
71
+ self.diff_level = diff_level
72
+ self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name, self.subgroup_property)
73
+ self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_property, self.subgroup_name) \
74
+ if self.is_break else ""
75
+ if subgroup_property in SUBGROUP_PROPERTY_IGNORED_LIST:
76
+ self.is_ignore = True
77
+ self.filter_key = [self.rule_id, self.subgroup_name, self.subgroup_property]
78
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message, self.is_ignore,
79
+ self.filter_key)
80
+
81
+
82
+ class SubgroupPropRemove(MetaChange):
83
+ def __init__(self, subgroup_name, subgroup_property, is_break=False, diff_level=DiffLevel.BREAK):
84
+ if not subgroup_name or not subgroup_property:
85
+ raise Exception("sub group name needed")
86
+ self.rule_id = "1014"
87
+ self.is_ignore = False
88
+ self.subgroup_name = subgroup_name
89
+ self.subgroup_property = subgroup_property
90
+ self.is_break = is_break
91
+ self.diff_level = diff_level
92
+ self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name, self.subgroup_property)
93
+ if self.is_break:
94
+ self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_property,
95
+ self.subgroup_name)
96
+ else:
97
+ self.suggest_message = ""
98
+ if subgroup_property in CMD_PROPERTY_IGNORED_LIST:
99
+ self.is_ignore = True
100
+ self.filter_key = [self.rule_id, self.subgroup_name, self.subgroup_property]
101
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message,
102
+ self.is_ignore, self.filter_key)
103
+
104
+
105
+ class SubgroupPropUpdate(MetaChange):
106
+
107
+ def __init__(self, subgroup_name, subgroup_property, is_break=False, diff_level=DiffLevel.INFO,
108
+ old_value=None, new_value=None):
109
+ if not subgroup_name or not subgroup_property:
110
+ raise Exception("sub group name and sub group prop needed")
111
+ self.rule_id = "1015"
112
+ self.is_ignore = False
113
+ self.subgroup_name = subgroup_name
114
+ self.is_break = is_break
115
+ self.diff_level = diff_level
116
+ self.subgroup_prop_updated = subgroup_property
117
+ self.old_value = ""
118
+ self.new_value = ""
119
+
120
+ if old_value is not None:
121
+ self.old_value = old_value
122
+ if new_value is not None:
123
+ self.new_value = new_value
124
+ self.rule_message = get_change_rule_template(self.rule_id).format(self.subgroup_name,
125
+ self.subgroup_prop_updated,
126
+ self.old_value, self.new_value)
127
+ if self.is_break:
128
+ self.suggest_message = get_change_suggest_template(self.rule_id).format(self.subgroup_prop_updated,
129
+ self.new_value, self.old_value,
130
+ self.subgroup_name)
131
+ else:
132
+ self.suggest_message = ""
133
+ if subgroup_property in SUBGROUP_PROPERTY_IGNORED_LIST:
134
+ self.is_ignore = True
135
+ self.filter_key = [self.rule_id, self.subgroup_name, self.subgroup_prop_updated]
136
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message,
137
+ self.is_ignore, self.filter_key)
56
138
 
57
139
 
58
140
  class CmdAdd(MetaChange):
59
- def __init__(self, cmd_name, is_break=False):
141
+ def __init__(self, cmd_name, is_break=False, diff_level=DiffLevel.INFO):
60
142
  if not cmd_name:
61
143
  raise Exception("cmd name needed")
62
144
  self.rule_id = "1001"
63
145
  self.cmd_name = cmd_name
64
146
  self.is_break = is_break
147
+ self.diff_level = diff_level
65
148
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name)
66
149
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.cmd_name) if self.is_break else ""
67
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
150
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
68
151
 
69
152
 
70
153
  class CmdRemove(MetaChange):
71
- def __init__(self, cmd_name, is_break=True):
154
+ def __init__(self, cmd_name, is_break=True, diff_level=DiffLevel.BREAK):
72
155
  if not cmd_name:
73
156
  raise Exception("cmd name needed")
74
157
  self.cmd_name = cmd_name
75
158
  self.rule_id = "1002"
76
159
  self.is_break = is_break
160
+ self.diff_level = diff_level
77
161
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name)
78
162
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.cmd_name) if self.is_break else ""
79
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
163
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
80
164
 
81
165
 
82
166
  class CmdPropAdd(MetaChange):
83
- def __init__(self, cmd_name, cmd_property, is_break=False):
167
+ def __init__(self, cmd_name, cmd_property, is_break=False, diff_level=DiffLevel.INFO):
84
168
  if not cmd_name or not cmd_property:
85
169
  raise Exception("cmd name needed")
86
170
  self.rule_id = "1003"
@@ -88,18 +172,19 @@ class CmdPropAdd(MetaChange):
88
172
  self.cmd_name = cmd_name
89
173
  self.cmd_property = cmd_property
90
174
  self.is_break = is_break
175
+ self.diff_level = diff_level
91
176
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.cmd_property)
92
177
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.cmd_property, self.cmd_name) \
93
178
  if self.is_break else ""
94
179
  if cmd_property in CMD_PROPERTY_IGNORED_LIST:
95
180
  self.is_ignore = True
96
181
  self.filter_key = [self.rule_id, self.cmd_name, self.cmd_property]
97
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message, self.is_ignore,
182
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message, self.is_ignore,
98
183
  self.filter_key)
99
184
 
100
185
 
101
186
  class CmdPropRemove(MetaChange):
102
- def __init__(self, cmd_name, cmd_property, is_break=False):
187
+ def __init__(self, cmd_name, cmd_property, is_break=False, diff_level=DiffLevel.BREAK):
103
188
  if not cmd_name or not cmd_property:
104
189
  raise Exception("cmd name needed")
105
190
  self.rule_id = "1004"
@@ -107,25 +192,28 @@ class CmdPropRemove(MetaChange):
107
192
  self.cmd_name = cmd_name
108
193
  self.cmd_property = cmd_property
109
194
  self.is_break = is_break
195
+ self.diff_level = diff_level
110
196
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.cmd_property)
111
197
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.cmd_property, self.cmd_name) \
112
198
  if self.is_break else ""
113
199
  if cmd_property in CMD_PROPERTY_IGNORED_LIST:
114
200
  self.is_ignore = True
115
201
  self.filter_key = [self.rule_id, self.cmd_name, self.cmd_property]
116
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message,
202
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message,
117
203
  self.is_ignore, self.filter_key)
118
204
 
119
205
 
120
206
  class CmdPropUpdate(MetaChange):
121
207
 
122
- def __init__(self, cmd_name, cmd_property, is_break=False, old_value=None, new_value=None):
208
+ def __init__(self, cmd_name, cmd_property, is_break=False, diff_level=DiffLevel.INFO,
209
+ old_value=None, new_value=None):
123
210
  if not cmd_name or not cmd_property:
124
211
  raise Exception("cmd name and cmd prop needed")
125
212
  self.rule_id = "1005"
126
213
  self.is_ignore = False
127
214
  self.cmd_name = cmd_name
128
215
  self.is_break = is_break
216
+ self.diff_level = diff_level
129
217
  self.cmd_prop_updated = cmd_property
130
218
  self.old_value = ""
131
219
  self.new_value = ""
@@ -142,43 +230,45 @@ class CmdPropUpdate(MetaChange):
142
230
  if cmd_property in CMD_PROPERTY_IGNORED_LIST:
143
231
  self.is_ignore = True
144
232
  self.filter_key = [self.rule_id, self.cmd_name, self.cmd_prop_updated]
145
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message,
233
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message,
146
234
  self.is_ignore, self.filter_key)
147
235
 
148
236
 
149
237
  class ParaAdd(MetaChange):
150
238
 
151
- def __init__(self, cmd_name, para_name, is_break=False):
239
+ def __init__(self, cmd_name, para_name, is_break=False, diff_level=DiffLevel.INFO):
152
240
  if not cmd_name or not para_name:
153
241
  raise Exception("cmd name, parameter name needed")
154
242
  self.rule_id = "1006"
155
243
  self.cmd_name = cmd_name
156
244
  self.para_name = para_name
157
245
  self.is_break = is_break
246
+ self.diff_level = diff_level
158
247
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.para_name)
159
248
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.para_name,
160
249
  self.cmd_name) if self.is_break else ""
161
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
250
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
162
251
 
163
252
 
164
253
  class ParaRemove(MetaChange):
165
254
 
166
- def __init__(self, cmd_name, para_name, is_break=False):
255
+ def __init__(self, cmd_name, para_name, is_break=False, diff_level=DiffLevel.BREAK):
167
256
  if not cmd_name or not para_name:
168
257
  raise Exception("cmd name, parameter name needed")
169
258
  self.rule_id = "1007"
170
259
  self.cmd_name = cmd_name
171
260
  self.para_name = para_name
172
261
  self.is_break = is_break
262
+ self.diff_level = diff_level
173
263
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.para_name)
174
264
  self.suggest_message = get_change_suggest_template(self.rule_id).format(self.para_name,
175
265
  self.cmd_name) if self.is_break else ""
176
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message)
266
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message)
177
267
 
178
268
 
179
269
  class ParaPropAdd(MetaChange):
180
270
 
181
- def __init__(self, cmd_name, para_name, para_property, para_prop_value, is_break=False):
271
+ def __init__(self, cmd_name, para_name, para_property, para_prop_value, is_break=False, diff_level=DiffLevel.INFO):
182
272
  if not cmd_name or not para_name or not para_property:
183
273
  raise Exception("cmd name, parameter name and parameter property needed")
184
274
  self.rule_id = "1008"
@@ -188,6 +278,7 @@ class ParaPropAdd(MetaChange):
188
278
  self.para_prop = para_property
189
279
  self.para_prop_value = para_prop_value
190
280
  self.is_break = is_break
281
+ self.diff_level = diff_level
191
282
 
192
283
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.para_name,
193
284
  self.para_prop, self.para_prop_value)
@@ -197,12 +288,12 @@ class ParaPropAdd(MetaChange):
197
288
  self.cmd_name) if self.is_break else ""
198
289
  if para_property in PARA_PROPERTY_IGNORED_LIST or para_name in PARA_NAME_IGNORED_LIST:
199
290
  self.is_ignore = True
200
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message, self.is_ignore)
291
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message, self.is_ignore)
201
292
 
202
293
 
203
294
  class ParaPropRemove(MetaChange):
204
295
 
205
- def __init__(self, cmd_name, para_name, para_property, para_prop_value, is_break=False):
296
+ def __init__(self, cmd_name, para_name, para_property, para_prop_value, is_break=False, diff_level=DiffLevel.BREAK):
206
297
  if not cmd_name or not para_name or not para_property:
207
298
  raise Exception("cmd name, parameter name and parameter property needed")
208
299
  self.rule_id = "1009"
@@ -212,6 +303,7 @@ class ParaPropRemove(MetaChange):
212
303
  self.para_prop = para_property
213
304
  self.para_prop_value = para_prop_value
214
305
  self.is_break = is_break
306
+ self.diff_level = diff_level
215
307
 
216
308
  self.rule_message = get_change_rule_template(self.rule_id).format(self.cmd_name, self.para_name,
217
309
  self.para_prop, self.para_prop_value)
@@ -221,12 +313,13 @@ class ParaPropRemove(MetaChange):
221
313
  self.cmd_name) if self.is_break else ""
222
314
  if para_property in PARA_PROPERTY_IGNORED_LIST or para_name in PARA_NAME_IGNORED_LIST:
223
315
  self.is_ignore = True
224
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message, self.is_ignore)
316
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message, self.is_ignore)
225
317
 
226
318
 
227
319
  class ParaPropUpdate(MetaChange):
228
320
 
229
- def __init__(self, cmd_name, para_name, para_property, is_break=False, old_value=None, new_value=None):
321
+ def __init__(self, cmd_name, para_name, para_property, is_break=False, diff_level=DiffLevel.INFO,
322
+ old_value=None, new_value=None):
230
323
  if not cmd_name or not para_name or not para_property:
231
324
  raise Exception("cmd name, parameter name and parameter property needed")
232
325
  self.rule_id = "1010"
@@ -235,6 +328,7 @@ class ParaPropUpdate(MetaChange):
235
328
  self.para_name = para_name
236
329
  self.para_prop_updated = para_property
237
330
  self.is_break = is_break
331
+ self.diff_level = diff_level
238
332
  self.old_value = None
239
333
  self.new_value = None
240
334
  if old_value is not None:
@@ -254,5 +348,5 @@ class ParaPropUpdate(MetaChange):
254
348
  if self.new_value in PARA_VALUE_IGNORED_LIST or self.old_value in PARA_VALUE_IGNORED_LIST:
255
349
  self.is_ignore = True
256
350
  self.filter_key = [self.rule_id, self.cmd_name, self.para_name, self.para_prop_updated]
257
- super().__init__(self.rule_id, is_break, self.rule_message, self.suggest_message,
351
+ super().__init__(self.rule_id, is_break, diff_level, self.rule_message, self.suggest_message,
258
352
  self.is_ignore, self.filter_key)
@@ -7,22 +7,35 @@
7
7
  import logging
8
8
  import os.path
9
9
 
10
- from .utils import get_command_tree, ChangeType, extract_cmd_name, extract_subgroup_name, extract_cmd_property
10
+ from .utils import get_command_tree, ChangeType, extract_cmd_name, extract_subgroup_name, extract_subgroup_property, \
11
+ extract_subgroup_deprecate_property, extract_cmd_property, extract_cmd_deprecate_property, DiffLevel
11
12
  from .meta_change import (CmdAdd, CmdRemove, CmdPropAdd, CmdPropRemove, CmdPropUpdate,
12
13
  ParaAdd, ParaRemove, ParaPropAdd, ParaPropRemove, ParaPropUpdate,
13
- SubgroupAdd, SubgroupRemove)
14
- from ._const import (CMD_PROPERTY_ADD_BREAK_LIST, CMD_PROPERTY_REMOVE_BREAK_LIST,
15
- CMD_PROPERTY_UPDATE_BREAK_LIST, PARA_PROPERTY_REMOVE_BREAK_LIST,
16
- PARA_PROPERTY_ADD_BREAK_LIST, PARA_PROPERTY_UPDATE_BREAK_LIST, META_CHANDE_WHITELIST_FILE_PATH)
14
+ SubgroupAdd, SubgroupRemove, SubgroupPropAdd, SubgroupPropRemove, SubgroupPropUpdate)
15
+ from ._const import (SUBGROUP_PROPERTY_ADD_BREAK_LIST, SUBGROUP_PROPERTY_ADD_WARN_LIST,
16
+ SUBGROUP_PROPERTY_REMOVE_BREAK_LIST, SUBGROUP_PROPERTY_REMOVE_WARN_LIST,
17
+ SUBGROUP_PROPERTY_UPDATE_BREAK_LIST, SUBGROUP_PROPERTY_UPDATE_WARN_LIST,
18
+ CMD_PROPERTY_ADD_BREAK_LIST, CMD_PROPERTY_ADD_WARN_LIST,
19
+ CMD_PROPERTY_REMOVE_BREAK_LIST, CMD_PROPERTY_REMOVE_WARN_LIST,
20
+ CMD_PROPERTY_UPDATE_BREAK_LIST, CMD_PROPERTY_UPDATE_WARN_LIST,
21
+ PARA_PROPERTY_REMOVE_BREAK_LIST, PARA_PROPERTY_REMOVE_WARN_LIST,
22
+ PARA_PROPERTY_ADD_BREAK_LIST, PARA_PROPERTY_ADD_WARN_LIST,
23
+ PARA_PROPERTY_UPDATE_BREAK_LIST, PARA_PROPERTY_UPDATE_WARN_LIST,
24
+ CMD_REMOVE_SUFFIX_WARN_LIST,
25
+ META_CHANDE_WHITELIST_FILE_PATH)
17
26
 
18
27
  logger = logging.getLogger(__name__)
19
28
 
20
29
 
21
30
  class MetaChangeDetect:
22
31
 
23
- EXPORTED_META_PROPERTY = ["rule_id", "rule_link_url", "is_break", "rule_message", "suggest_message", "cmd_name", "subgroup_name"]
32
+ EXPORTED_META_PROPERTY = ["rule_id", "rule_link_url", "is_break", "diff_level",
33
+ "rule_message", "suggest_message", "cmd_name", "subgroup_name"]
24
34
  CHECKED_PARA_PROPERTY = ["name", "options", "required", "choices", "id_part", "nargs", "default", "desc",
25
- "aaz_type", "type", "aaz_default", "aaz_choices"]
35
+ "aaz_type", "type", "aaz_default", "aaz_choices",
36
+ "deprecate_info_target", "deprecate_info_redirect", "deprecate_info_hide",
37
+ "deprecate_info_expiration",
38
+ "options_deprecate_info"]
26
39
 
27
40
  def __init__(self, deep_diff=None, base_meta=None, diff_meta=None):
28
41
  self.deep_diff = {}
@@ -68,6 +81,69 @@ class MetaChangeDetect:
68
81
  def __log_cmd_with_parameter_change(self, cmd_name):
69
82
  self.cmd_set_with_parameter_change.add(cmd_name)
70
83
 
84
+ def __process_subgroup_items(self, dict_key, subgroup_name, diff_type):
85
+ has_subgroup_key, subgroup_property = extract_subgroup_property(dict_key, subgroup_name)
86
+ if not has_subgroup_key:
87
+ if diff_type == ChangeType.REMOVE:
88
+ diff_obj = SubgroupRemove(subgroup_name)
89
+ else:
90
+ diff_obj = SubgroupAdd(subgroup_name)
91
+ self.diff_objs.append(diff_obj)
92
+ return
93
+
94
+ # deal with deprecate_info expanded key's add/remove/update
95
+ if diff_type == ChangeType.ADD:
96
+ if subgroup_property in SUBGROUP_PROPERTY_ADD_WARN_LIST:
97
+ diff_obj = SubgroupPropAdd(subgroup_name, subgroup_property, False, DiffLevel.WARN)
98
+ elif subgroup_property in SUBGROUP_PROPERTY_ADD_BREAK_LIST:
99
+ diff_obj = SubgroupPropAdd(subgroup_name, subgroup_property, True, DiffLevel.BREAK)
100
+ else:
101
+ diff_obj = SubgroupPropAdd(subgroup_name, subgroup_property, False, DiffLevel.INFO)
102
+ self.diff_objs.append(diff_obj)
103
+ else:
104
+ if subgroup_property in SUBGROUP_PROPERTY_REMOVE_WARN_LIST:
105
+ diff_obj = SubgroupPropRemove(subgroup_name, subgroup_property, False, DiffLevel.WARN)
106
+ elif subgroup_property in SUBGROUP_PROPERTY_REMOVE_BREAK_LIST:
107
+ diff_obj = SubgroupPropRemove(subgroup_name, subgroup_property, True, DiffLevel.BREAK)
108
+ else:
109
+ diff_obj = SubgroupPropRemove(subgroup_name, subgroup_property, False, DiffLevel.INFO)
110
+ self.diff_objs.append(diff_obj)
111
+
112
+ def __process_cmd_items(self, dict_key, cmd_name, diff_type):
113
+ has_cmd_key, cmd_property = extract_cmd_property(dict_key, cmd_name)
114
+ if not has_cmd_key:
115
+ if diff_type == ChangeType.REMOVE:
116
+ if cmd_name.split()[-1] in CMD_REMOVE_SUFFIX_WARN_LIST:
117
+ diff_obj = CmdRemove(cmd_name, False, DiffLevel.WARN)
118
+ else:
119
+ diff_obj = CmdRemove(cmd_name, True, DiffLevel.BREAK)
120
+ else:
121
+ diff_obj = CmdAdd(cmd_name)
122
+
123
+ self.diff_objs.append(diff_obj)
124
+ return
125
+
126
+ if cmd_property == "parameters":
127
+ self.__log_cmd_with_parameter_change(cmd_name)
128
+ return
129
+
130
+ if diff_type == ChangeType.ADD:
131
+ if cmd_property in CMD_PROPERTY_ADD_WARN_LIST:
132
+ diff_obj = CmdPropAdd(cmd_name, cmd_property, False, DiffLevel.WARN)
133
+ elif cmd_property in CMD_PROPERTY_ADD_BREAK_LIST:
134
+ diff_obj = CmdPropAdd(cmd_name, cmd_property, True, DiffLevel.BREAK)
135
+ else:
136
+ diff_obj = CmdPropAdd(cmd_name, cmd_property, False, DiffLevel.INFO)
137
+ self.diff_objs.append(diff_obj)
138
+ else:
139
+ if cmd_property in CMD_PROPERTY_REMOVE_WARN_LIST:
140
+ diff_obj = CmdPropRemove(cmd_name, cmd_property, False, DiffLevel.WARN)
141
+ elif cmd_property in CMD_PROPERTY_REMOVE_BREAK_LIST:
142
+ diff_obj = CmdPropRemove(cmd_name, cmd_property, True, DiffLevel.BREAK)
143
+ else:
144
+ diff_obj = CmdPropRemove(cmd_name, cmd_property, False, DiffLevel.INFO)
145
+ self.diff_objs.append(diff_obj)
146
+
71
147
  def __iter_dict_items(self, dict_items, diff_type):
72
148
  if diff_type not in [ChangeType.REMOVE, ChangeType.ADD]:
73
149
  raise Exception("Unsupported dict item type")
@@ -78,42 +154,16 @@ class MetaChangeDetect:
78
154
  has_subgroup, subgroup_name = extract_subgroup_name(dict_key)
79
155
  if not has_subgroup or not subgroup_name:
80
156
  continue
81
- if diff_type == ChangeType.REMOVE:
82
- diff_obj = SubgroupRemove(subgroup_name)
83
- else:
84
- diff_obj = SubgroupAdd(subgroup_name)
85
- self.diff_objs.append(diff_obj)
157
+ self.__process_subgroup_items(dict_key, subgroup_name, diff_type)
86
158
  continue
87
-
88
- has_cmd_key, cmd_property = extract_cmd_property(dict_key, cmd_name)
89
- if has_cmd_key:
90
- if cmd_property == "parameters":
91
- self.__log_cmd_with_parameter_change(cmd_name)
92
- elif diff_type == ChangeType.ADD:
93
- if cmd_property in CMD_PROPERTY_ADD_BREAK_LIST:
94
- diff_obj = CmdPropAdd(cmd_name, cmd_property, True)
95
- else:
96
- diff_obj = CmdPropAdd(cmd_name, cmd_property, False)
97
- self.diff_objs.append(diff_obj)
98
- else:
99
- if cmd_property in CMD_PROPERTY_REMOVE_BREAK_LIST:
100
- diff_obj = CmdPropRemove(cmd_name, cmd_property, True)
101
- else:
102
- diff_obj = CmdPropRemove(cmd_name, cmd_property, False)
103
- self.diff_objs.append(diff_obj)
104
- else:
105
- if diff_type == ChangeType.REMOVE:
106
- diff_obj = CmdRemove(cmd_name)
107
- else:
108
- diff_obj = CmdAdd(cmd_name)
109
-
110
- self.diff_objs.append(diff_obj)
159
+ self.__process_cmd_items(dict_key, cmd_name, diff_type)
111
160
 
112
161
  def __iter_list_items(self, list_items, diff_type):
113
162
  """
163
+ ['parameters'][3]
114
164
  ['parameters'][0]['options'][1]
115
165
  ['parameters'][0]['choices'][0]
116
- ['parameters'][3]
166
+ ['parameters'][5]['options_deprecate_info'][1]
117
167
  """
118
168
  if diff_type not in [ChangeType.REMOVE, ChangeType.ADD]:
119
169
  raise Exception("Unsupported dict item type")
@@ -154,7 +204,41 @@ class MetaChangeDetect:
154
204
  list_item_add = self.deep_diff["iterable_item_added"]
155
205
  self.__iter_list_items(list_item_add, ChangeType.ADD)
156
206
 
207
+ def __process_subgroup_value_change(self, key, subgroup_name, old_value, new_value):
208
+ has_subgroup_prop, subgroup_property = extract_subgroup_property(key, subgroup_name)
209
+ if not has_subgroup_prop:
210
+ # subgroup key does not change independently, it must be followed by some cmd property
211
+ return
212
+ if subgroup_property in SUBGROUP_PROPERTY_UPDATE_WARN_LIST:
213
+ diff_obj = SubgroupPropUpdate(subgroup_name, subgroup_property, False, DiffLevel.WARN, old_value, new_value)
214
+ elif subgroup_property in SUBGROUP_PROPERTY_UPDATE_BREAK_LIST:
215
+ diff_obj = SubgroupPropUpdate(subgroup_name, subgroup_property, True, DiffLevel.BREAK, old_value, new_value)
216
+ else:
217
+ diff_obj = SubgroupPropUpdate(subgroup_name, subgroup_property, False, DiffLevel.INFO, old_value, new_value)
218
+ self.diff_objs.append(diff_obj)
219
+
220
+ def __process_cmd_value_change(self, key, cmd_name, old_value, new_value):
221
+ has_cmd_prop, cmd_property = extract_cmd_property(key, cmd_name)
222
+ if not has_cmd_prop:
223
+ # cmd key does not change independently, it must followed by some cmd property
224
+ return
225
+ if cmd_property == "parameters":
226
+ self.__log_cmd_with_parameter_change(cmd_name)
227
+ return
228
+
229
+ if cmd_property in CMD_PROPERTY_UPDATE_WARN_LIST:
230
+ diff_obj = CmdPropUpdate(cmd_name, cmd_property, False, DiffLevel.WARN, old_value, new_value)
231
+ elif cmd_property in CMD_PROPERTY_UPDATE_BREAK_LIST:
232
+ diff_obj = CmdPropUpdate(cmd_name, cmd_property, True, DiffLevel.BREAK, old_value, new_value)
233
+ else:
234
+ diff_obj = CmdPropUpdate(cmd_name, cmd_property, False, DiffLevel.INFO, old_value, new_value)
235
+ self.diff_objs.append(diff_obj)
236
+
157
237
  def check_value_change(self):
238
+ """
239
+ ['sub_groups']['acr']['deprecate_info_redirect']
240
+ ['commands']['acr helm show']['deprecate_info_redirect']
241
+ """
158
242
  if not self.deep_diff.get("values_changed", None):
159
243
  return
160
244
  value_changes = self.deep_diff["values_changed"]
@@ -163,20 +247,13 @@ class MetaChangeDetect:
163
247
  new_value = value_obj["new_value"]
164
248
  has_cmd, cmd_name = extract_cmd_name(key)
165
249
  if not has_cmd or not cmd_name:
166
- print("extract cmd failed for " + key)
250
+ has_subgroup, subgroup_name = extract_subgroup_name(key)
251
+ if not has_subgroup or not subgroup_name:
252
+ print("extract cmd or sub group failed for " + key)
253
+ continue
254
+ self.__process_subgroup_value_change(key, subgroup_name, old_value, new_value)
167
255
  continue
168
-
169
- has_cmd_prop, cmd_property = extract_cmd_property(key, cmd_name)
170
- if not has_cmd_prop:
171
- return
172
- if cmd_property == "parameters":
173
- self.__log_cmd_with_parameter_change(cmd_name)
174
- else:
175
- if cmd_property in CMD_PROPERTY_UPDATE_BREAK_LIST:
176
- diff_obj = CmdPropUpdate(cmd_name, cmd_property, True, old_value, new_value)
177
- else:
178
- diff_obj = CmdPropUpdate(cmd_name, cmd_property, False, old_value, new_value)
179
- self.diff_objs.append(diff_obj)
256
+ self.__process_cmd_value_change(key, cmd_name, old_value, new_value)
180
257
 
181
258
  @staticmethod
182
259
  def __search_para_with_name_and_options(base_para_obj, search_parameters):
@@ -192,6 +269,39 @@ class MetaChangeDetect:
192
269
  return para_obj
193
270
  return None
194
271
 
272
+ def __process_parameter_value_update(self, cmd_name, prop, base_para_obj, base_val, cmp_val):
273
+ if base_val == cmp_val:
274
+ return
275
+ if prop == "options_deprecate_info":
276
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, DiffLevel.INFO,
277
+ base_val, cmp_val)
278
+ self.diff_objs.append(diff_obj)
279
+ return
280
+ if isinstance(base_val, list) and isinstance(cmp_val, list):
281
+ if set(base_val).issubset(set(cmp_val)):
282
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, DiffLevel.INFO,
283
+ base_val, cmp_val)
284
+ else:
285
+ if prop in PARA_PROPERTY_UPDATE_WARN_LIST:
286
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, DiffLevel.WARN,
287
+ base_val, cmp_val)
288
+ else:
289
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, True, DiffLevel.BREAK,
290
+ base_val, cmp_val)
291
+ self.diff_objs.append(diff_obj)
292
+ return
293
+
294
+ if prop in PARA_PROPERTY_UPDATE_WARN_LIST:
295
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, DiffLevel.WARN,
296
+ base_val, cmp_val)
297
+ elif prop in PARA_PROPERTY_UPDATE_BREAK_LIST:
298
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, True, DiffLevel.BREAK,
299
+ base_val, cmp_val)
300
+ else:
301
+ diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, DiffLevel.INFO,
302
+ base_val, cmp_val)
303
+ self.diff_objs.append(diff_obj)
304
+
195
305
  def check_cmd_parameter_diff(self, cmd_name, base_parameters, cmp_parameters):
196
306
  """check cmd parameter diff"""
197
307
  for base_para_obj in base_parameters:
@@ -199,7 +309,7 @@ class MetaChangeDetect:
199
309
  cmp_para_obj = self.__search_para_with_name_and_options(base_para_obj, cmp_parameters)
200
310
  if cmp_para_obj is None:
201
311
  # cmd lost parameter obj, is break
202
- diff_obj = ParaRemove(cmd_name, base_para_obj["name"], True)
312
+ diff_obj = ParaRemove(cmd_name, base_para_obj["name"], True, DiffLevel.BREAK)
203
313
  # add flag to avoid duplicate compare
204
314
  self.diff_objs.append(diff_obj)
205
315
  continue
@@ -210,36 +320,36 @@ class MetaChangeDetect:
210
320
  if prop in base_para_obj and prop not in cmp_para_obj:
211
321
  # prop dropped in new para obj
212
322
  prop_value = base_para_obj[prop]
213
- if prop in PARA_PROPERTY_REMOVE_BREAK_LIST:
214
- diff_obj = ParaPropRemove(cmd_name, base_para_obj["name"], prop, prop_value, True)
323
+ if prop in PARA_PROPERTY_REMOVE_WARN_LIST:
324
+ diff_obj = ParaPropRemove(cmd_name, base_para_obj["name"], prop, prop_value,
325
+ False, DiffLevel.WARN)
326
+ elif prop in PARA_PROPERTY_REMOVE_BREAK_LIST:
327
+ diff_obj = ParaPropRemove(cmd_name, base_para_obj["name"], prop, prop_value,
328
+ True, DiffLevel.BREAK)
215
329
  else:
216
- diff_obj = ParaPropRemove(cmd_name, base_para_obj["name"], prop, prop_value, False)
330
+ diff_obj = ParaPropRemove(cmd_name, base_para_obj["name"], prop, prop_value,
331
+ False, DiffLevel.INFO)
217
332
  self.diff_objs.append(diff_obj)
218
333
  continue
219
334
  if prop not in base_para_obj and prop in cmp_para_obj:
220
335
  # prop added in new para obj
221
336
  prop_value = cmp_para_obj[prop]
222
- if prop in PARA_PROPERTY_ADD_BREAK_LIST:
223
- diff_obj = ParaPropAdd(cmd_name, base_para_obj["name"], prop, prop_value, True)
337
+ if prop in PARA_PROPERTY_ADD_WARN_LIST:
338
+ diff_obj = ParaPropAdd(cmd_name, base_para_obj["name"], prop, prop_value,
339
+ False, DiffLevel.WARN)
340
+ elif prop in PARA_PROPERTY_ADD_BREAK_LIST:
341
+ diff_obj = ParaPropAdd(cmd_name, base_para_obj["name"], prop, prop_value,
342
+ True, DiffLevel.BREAK)
224
343
  else:
225
- diff_obj = ParaPropAdd(cmd_name, base_para_obj["name"], prop, prop_value, False)
344
+ diff_obj = ParaPropAdd(cmd_name, base_para_obj["name"], prop, prop_value,
345
+ False, DiffLevel.INFO)
226
346
  self.diff_objs.append(diff_obj)
227
347
  continue
228
348
  # prop exists in both new and old para obj, value needs to be checked
229
349
  base_val = base_para_obj[prop]
230
350
  cmp_val = cmp_para_obj[prop]
231
-
232
- if base_val != cmp_val:
233
- if isinstance(base_val, list) and isinstance(cmp_val, list):
234
- if set(base_val).issubset(set(cmp_val)):
235
- diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, base_val, cmp_val)
236
- else:
237
- diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, True, base_val, cmp_val)
238
- elif prop in PARA_PROPERTY_UPDATE_BREAK_LIST:
239
- diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, True, base_val, cmp_val)
240
- else:
241
- diff_obj = ParaPropUpdate(cmd_name, base_para_obj["name"], prop, False, base_val, cmp_val)
242
- self.diff_objs.append(diff_obj)
351
+ self.__process_parameter_value_update(cmd_name, prop, base_para_obj, base_val, cmp_val)
352
+ continue
243
353
 
244
354
  # check added parameter obj, if obj is required, then is break
245
355
  for cmp_para_obj in cmp_parameters:
@@ -248,9 +358,9 @@ class MetaChangeDetect:
248
358
  para_name = cmp_para_obj["name"]
249
359
  required = cmp_para_obj.get("required", None)
250
360
  if required:
251
- diff_obj = ParaAdd(cmd_name, para_name, True)
361
+ diff_obj = ParaAdd(cmd_name, para_name, True, DiffLevel.BREAK)
252
362
  else:
253
- diff_obj = ParaAdd(cmd_name, para_name, False)
363
+ diff_obj = ParaAdd(cmd_name, para_name, False, DiffLevel.INFO)
254
364
  self.diff_objs.append(diff_obj)
255
365
 
256
366
  def check_cmds_parameter_diff(self):
@@ -276,6 +386,9 @@ class MetaChangeDetect:
276
386
  self.diff_objs = new_diff_objs
277
387
 
278
388
  def check_deep_diffs(self):
389
+ for diff_type, diff_key in self.deep_diff.items():
390
+ for it in diff_key:
391
+ print(diff_type, ": ", it)
279
392
  self.check_dict_item_remove()
280
393
  self.check_dict_item_add()
281
394
  self.check_list_item_remove()
@@ -54,6 +54,13 @@ class ChangeType(int, Enum):
54
54
  REMOVE = 3
55
55
 
56
56
 
57
+ class DiffLevel(int, Enum):
58
+ DEFAULT = 0
59
+ INFO = 1
60
+ WARN = 2
61
+ BREAK = 3
62
+
63
+
57
64
  def get_command_tree(command_name):
58
65
  """
59
66
  input: monitor log-profiles create
@@ -158,6 +165,22 @@ def extract_subgroup_name(key):
158
165
  return True, subgroup_ame_res[-1]
159
166
 
160
167
 
168
+ def extract_subgroup_property(key, subgroup_name):
169
+ subgroup_key_pattern = re.compile(subgroup_name + r"\'\]\[\'([a-zA-Z0-9\-\_]+)\'\]")
170
+ subgroup_key_res = re.findall(subgroup_key_pattern, key)
171
+ if not subgroup_key_res or len(subgroup_key_res) == 0:
172
+ return False, None
173
+ return True, subgroup_key_res[0]
174
+
175
+
176
+ def extract_subgroup_deprecate_property(key, deprecate_key):
177
+ subgroup_deprecate_key_pattern = re.compile(deprecate_key + r"\'\]\[\'([a-zA-Z0-9\-\_]+)\'\]")
178
+ subgroup_deprecate_key_res = re.findall(subgroup_deprecate_key_pattern, key)
179
+ if not subgroup_deprecate_key_res or len(subgroup_deprecate_key_res) == 0:
180
+ return False, None
181
+ return True, subgroup_deprecate_key_res[0]
182
+
183
+
161
184
  def extract_cmd_name(key):
162
185
  cmd_name_res = re.findall(CMD_NAME_PATTERN, key)
163
186
  if not cmd_name_res or len(cmd_name_res) == 0:
@@ -173,6 +196,13 @@ def extract_cmd_property(key, cmd_name):
173
196
  return True, cmd_key_res[0]
174
197
 
175
198
 
199
+ def extract_cmd_deprecate_property(key, deprecate_key):
200
+ cmd_deprecate_key_pattern = re.compile(deprecate_key + r"\'\]\[\'([a-zA-Z0-9\-\_]+)\'\]")
201
+ cmd_deprecate_key_res = re.findall(cmd_deprecate_key_pattern, key)
202
+ if not cmd_deprecate_key_res or len(cmd_deprecate_key_res) == 0:
203
+ return False, None
204
+ return True, cmd_deprecate_key_res[0]
205
+
176
206
  def extract_para_info(key):
177
207
  parameters_ind = key.find("['parameters']")
178
208
  property_ind = key.find("[", parameters_ind + 1)
@@ -244,3 +274,22 @@ def export_meta_changes_to_dict(module_diffs, version_diff_file):
244
274
  writer = csv.writer(f)
245
275
  writer.writerows(dict_res)
246
276
  return None
277
+
278
+
279
+ def expand_deprecate_obj(meta_obj):
280
+ if type(meta_obj) is not dict:
281
+ return
282
+ for key, obj in meta_obj.items():
283
+ if type(obj) is dict:
284
+ if obj.get("deprecate_info", None):
285
+ for dekey, devalue in obj["deprecate_info"].items():
286
+ obj["deprecate_info_" + dekey] = devalue
287
+ del obj["deprecate_info"]
288
+ expand_deprecate_obj(obj)
289
+ elif type(obj) is list:
290
+ for ind, item in enumerate(obj):
291
+ if type(item) is dict and item.get("deprecate_info", None):
292
+ for dekey, devalue in item["deprecate_info"].items():
293
+ obj[ind]["deprecate_info_" + dekey] = devalue
294
+ del item["deprecate_info"]
295
+ expand_deprecate_obj(item)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-cli-diff-tool
3
- Version: 0.0.4
3
+ Version: 0.0.6
4
4
  Summary: A tool for cli metadata management
5
5
  Author: Microsoft Corporation
6
6
  Author-email: azpycli@microsoft.com
@@ -98,6 +98,15 @@ License
98
98
 
99
99
  Release History
100
100
  ===============
101
+ 0.0.6
102
+ ++++++
103
+ * Add diff support for deprecate_info in subgroup, cmd, parameters and options
104
+
105
+ 0.0.5
106
+ ++++++
107
+ * Add `DiffLevel` to meta comparison
108
+ * Downgrade change level according to warn list
109
+
101
110
  0.0.4
102
111
  ++++++
103
112
  * Add meta change parameter update warn list
@@ -0,0 +1,168 @@
1
+ # -----------------------------------------------------------------------------
2
+ # Copyright (c) Microsoft Corporation. All rights reserved.
3
+ # Licensed under the MIT License. See License.txt in the project root for
4
+ # license information.
5
+ # -----------------------------------------------------------------------------
6
+ # pylint: disable=line-too-long
7
+
8
+ import unittest
9
+ import os
10
+ from azure_cli_diff_tool import meta_diff
11
+ from azure_cli_diff_tool.utils import get_command_tree, extract_cmd_name, extract_cmd_property, extract_para_info, \
12
+ extract_subgroup_name, extract_subgroup_deprecate_property, expand_deprecate_obj
13
+
14
+
15
+ class MyTestCase(unittest.TestCase):
16
+
17
+ def test_parse_cmd_tree(self):
18
+ cmd_name = "monitor log-profiles create"
19
+ ret = get_command_tree(cmd_name)
20
+ self.assertTrue(ret["is_group"], "group parse failed")
21
+ self.assertFalse(ret["sub_info"]["sub_info"]["is_group"], "group parse failed")
22
+ self.assertTrue(ret["sub_info"]["sub_info"]["cmd_name"] == "monitor log-profiles create", "group parse failed")
23
+
24
+ def test_diff_dict_key_parse(self):
25
+ test_key = "root['sub_groups']['monitor']['sub_groups']['monitor private-link-scope']['sub_groups']" \
26
+ "['monitor private-link-scope scoped-resource']['commands']" \
27
+ "['monitor private-link-scope scoped-resource list']['parameters'][0]['options'][1]"
28
+ has_cmd, cmd_name = extract_cmd_name(test_key)
29
+ self.assertTrue(has_cmd, "cmd parse failed from diff dict key")
30
+ self.assertEqual(cmd_name, "monitor private-link-scope scoped-resource list", "cmd name extract failed")
31
+ has_cmd_key, cmd_key = extract_cmd_property(test_key, cmd_name)
32
+ self.assertTrue(has_cmd_key, "cmd property parse failed from diff dict key")
33
+ self.assertEqual(cmd_key, "parameters", "cmd property extract failed")
34
+ para_res = extract_para_info(test_key)
35
+ self.assertEqual(para_res[0], "0", "cmd parameter parse failed")
36
+
37
+ def test_diff_dict_key_for_subgroups(self):
38
+ test_key = "root['sub_groups']['monitor']['sub_groups']['monitor account']"
39
+ has_cmd, _ = extract_cmd_name(test_key)
40
+ self.assertFalse(has_cmd, "cmd parse error from diff dict key")
41
+ has_subgroup, subgroup_name = extract_subgroup_name(test_key)
42
+ self.assertTrue(has_subgroup, "sub group parse failed from diff dict key")
43
+ self.assertEqual(subgroup_name, "monitor account", "sub group name extract failed")
44
+
45
+ def test_diff_dict_key_for_subgroups_deprecate_info(self):
46
+ test_key = "root['sub_groups']['acr']['deprecate_info']['hide']"
47
+ has_cmd, _ = extract_cmd_name(test_key)
48
+ self.assertFalse(has_cmd, "cmd parse error from diff dict key")
49
+ has_subgroup, subgroup_name = extract_subgroup_name(test_key)
50
+ self.assertTrue(has_subgroup, "sub group parse failed from diff dict key")
51
+ self.assertEqual(subgroup_name, "acr", "sub group name extract failed")
52
+ has_subgroup_deprecate_prop, subgroup_deprecate_prop = extract_subgroup_deprecate_property(test_key, "deprecate_info")
53
+ self.assertTrue(has_subgroup_deprecate_prop, "sub group deprecate info prop parse failed from diff dict key")
54
+ self.assertEqual(subgroup_deprecate_prop, "hide", "sub group deprecate prop extract failed")
55
+
56
+ def test_diff_meta(self):
57
+ if not os.path.exists("./jsons/az_monitor_meta_before.json") \
58
+ or not os.path.exists("./jsons/az_monitor_meta_after.json"):
59
+ return
60
+ result = meta_diff(base_meta_file="./jsons/az_monitor_meta_before.json",
61
+ diff_meta_file="./jsons/az_monitor_meta_after.json",
62
+ output_type="text")
63
+ target_message = [
64
+ "please confirm cmd `monitor private-link-scope scoped-resource show` removed",
65
+ "sub group `monitor private-link-scope private-endpoint-connection cust` removed",
66
+ "cmd `monitor private-link-scope private-link-resource list` update parameter `scope_name`: added property `type=string`",
67
+ "cmd `monitor private-link-scope private-link-resource list` update parameter `resource_group_name`: removed property `id_part=resource_group` | diff_level: 2"
68
+ ]
69
+ for mes in target_message:
70
+ found = False
71
+ for line in result:
72
+ if line.find(mes) > -1:
73
+ found = True
74
+ break
75
+ self.assertTrue(found, "target message not found")
76
+
77
+ ignored_message = [
78
+ "updated property `is_aaz` from `False` to `True`"
79
+ ]
80
+ for mes in ignored_message:
81
+ ignored = True
82
+ for line in result:
83
+ if line.find(mes) > -1:
84
+ ignored = False
85
+ break
86
+ self.assertTrue(ignored, "ignored message found")
87
+
88
+ def test_diff_meta_whitelist(self):
89
+ if not os.path.exists("./jsons/az_ams_meta_before.json") \
90
+ or not os.path.exists("./jsons/az_ams_meta_after.json"):
91
+ return
92
+ result = meta_diff(base_meta_file="./jsons/az_ams_meta_before.json",
93
+ diff_meta_file="./jsons/az_ams_meta_after.json",
94
+ output_type="text")
95
+ self.assertEqual(result, [], "returned change isn't empty")
96
+
97
+ def test_expand_deprecate_obj(self):
98
+ example_obj = {
99
+ "module_name": "acr",
100
+ "name": "az",
101
+ "commands": {},
102
+ "sub_groups": {
103
+ "acr": {
104
+ "name": "acr",
105
+ "commands": {
106
+ "acr helm list": {
107
+ "name": "acr helm list",
108
+ "parameters": [{
109
+ "name": "resource_group_name",
110
+ "deprecate_info": {
111
+ "redirect": "resource_group_name2",
112
+ },
113
+ "options": ["--resource-group", "-g"],
114
+ "id_part": "resource_group"
115
+ }, {
116
+ "name": "password",
117
+ "options": ["--password", "-p"],
118
+ "options_deprecate_info": [{
119
+ "target": "--password",
120
+ "redirect": "--registry-password",
121
+ }]
122
+ }]
123
+ }
124
+ },
125
+ "deprecate_info": {
126
+ "target": "acr",
127
+ "redirect": "acr3",
128
+ "hide": True
129
+ }
130
+ }
131
+ }
132
+ }
133
+ expand_deprecate_obj(example_obj)
134
+ self.assertEqual(example_obj["sub_groups"]["acr"]["deprecate_info_target"], "acr",
135
+ "deprecate info expand not working properly")
136
+ self.assertEqual(example_obj["sub_groups"]["acr"]["deprecate_info_redirect"], "acr3",
137
+ "deprecate info expand not working properly")
138
+ self.assertEqual(example_obj["sub_groups"]["acr"]["commands"]["acr helm list"]["parameters"][0]["deprecate_info_redirect"], "resource_group_name2", "deprecate info expand not working properly")
139
+
140
+ def test_diff_meta_for_deprecate_info(self):
141
+ if not os.path.exists("./jsons/az_acr_meta_before.json") or not os.path.exists("./jsons/az_acr_meta_after.json"):
142
+ return
143
+ result = meta_diff(base_meta_file="./jsons/az_acr_meta_before.json",
144
+ diff_meta_file="./jsons/az_acr_meta_after.json",
145
+ output_type="text")
146
+ target_message = [
147
+ "sub group `acr` added property `deprecate_info_hide` | diff_level: 2",
148
+ "sub group `acr` updated property `deprecate_info_redirect` from `acr2` to `acr3` | diff_level: 1",
149
+ "sub group `acr helm` removed property `deprecate_info_target` | diff_level: 1",
150
+ "sub group `acr helm` removed property `deprecate_info_redirect` | diff_level: 2",
151
+ "cmd `acr helm list` removed property `deprecate_info_target` | diff_level: 1",
152
+ "cmd `acr helm list` removed property `deprecate_info_redirect` | diff_level: 2",
153
+ "cmd `acr helm list` removed property `deprecate_info_hide` | diff_level: 1",
154
+ "cmd `acr helm show` updated property `deprecate_info_redirect` from `acr helm show2` to `acr helm show3` | diff_level: 2",
155
+ "cmd `acr helm list` update parameter `repository`: removed property `options_deprecate_info=",
156
+ ]
157
+ for mes in target_message:
158
+ found = False
159
+ for line in result:
160
+ if line.find(mes) > -1:
161
+ found = True
162
+ break
163
+ print(mes)
164
+ self.assertTrue(found, "target message not found")
165
+
166
+
167
+ if __name__ == '__main__':
168
+ unittest.main()
@@ -1,88 +0,0 @@
1
- # -----------------------------------------------------------------------------
2
- # Copyright (c) Microsoft Corporation. All rights reserved.
3
- # Licensed under the MIT License. See License.txt in the project root for
4
- # license information.
5
- # -----------------------------------------------------------------------------
6
- # pylint: disable=line-too-long
7
-
8
- import unittest
9
- import os
10
- from azure_cli_diff_tool import meta_diff
11
- from azure_cli_diff_tool.utils import get_command_tree, extract_cmd_name, extract_cmd_property, extract_para_info, \
12
- extract_subgroup_name
13
-
14
-
15
- class MyTestCase(unittest.TestCase):
16
-
17
- def test_parse_cmd_tree(self):
18
- cmd_name = "monitor log-profiles create"
19
- ret = get_command_tree(cmd_name)
20
- self.assertTrue(ret["is_group"], "group parse failed")
21
- self.assertFalse(ret["sub_info"]["sub_info"]["is_group"], "group parse failed")
22
- self.assertTrue(ret["sub_info"]["sub_info"]["cmd_name"] == "monitor log-profiles create", "group parse failed")
23
-
24
- def test_diff_dict_key_parse(self):
25
- test_key = "root['sub_groups']['monitor']['sub_groups']['monitor private-link-scope']['sub_groups']" \
26
- "['monitor private-link-scope scoped-resource']['commands']" \
27
- "['monitor private-link-scope scoped-resource list']['parameters'][0]['options'][1]"
28
- has_cmd, cmd_name = extract_cmd_name(test_key)
29
- self.assertTrue(has_cmd, "cmd parse failed from diff dict key")
30
- self.assertEqual(cmd_name, "monitor private-link-scope scoped-resource list", "cmd name extract failed")
31
- has_cmd_key, cmd_key = extract_cmd_property(test_key, cmd_name)
32
- self.assertTrue(has_cmd_key, "cmd property parse failed from diff dict key")
33
- self.assertEqual(cmd_key, "parameters", "cmd property extract failed")
34
- para_res = extract_para_info(test_key)
35
- self.assertEqual(para_res[0], "0", "cmd parameter parse failed")
36
-
37
- def test_diff_dict_key_for_subgroups(self):
38
- test_key = "root['sub_groups']['monitor']['sub_groups']['monitor account']"
39
- has_cmd, _ = extract_cmd_name(test_key)
40
- self.assertFalse(has_cmd, "cmd parse error from diff dict key")
41
- has_subgroup, subgroup_name = extract_subgroup_name(test_key)
42
- self.assertTrue(has_subgroup, "sub group parse failed from diff dict key")
43
- self.assertEqual(subgroup_name, "monitor account", "sub group name extract failed")
44
-
45
- def test_diff_meta(self):
46
- if not os.path.exists("./jsons/az_monitor_meta_before.json") \
47
- or not os.path.exists("./jsons/az_monitor_meta_after.json"):
48
- return
49
- result = meta_diff(base_meta_file="./jsons/az_monitor_meta_before.json",
50
- diff_meta_file="./jsons/az_monitor_meta_after.json",
51
- output_type="text")
52
- target_message = [
53
- "please confirm cmd `monitor private-link-scope scoped-resource show` removed",
54
- "sub group `monitor private-link-scope private-endpoint-connection cust` removed",
55
- "cmd `monitor private-link-scope private-link-resource list` update parameter `scope_name`: added property `type=string`",
56
- "cmd `monitor private-link-scope private-link-resource list` update parameter `resource_group_name`: removed property `id_part=resource_group`"
57
- ]
58
- for mes in target_message:
59
- found = False
60
- for line in result:
61
- if line.find(mes) > -1:
62
- found = True
63
- break
64
- self.assertTrue(found, "target message not found")
65
-
66
- ignored_message = [
67
- "updated property `is_aaz` from `False` to `True`"
68
- ]
69
- for mes in ignored_message:
70
- ignored = True
71
- for line in result:
72
- if line.find(mes) > -1:
73
- ignored = False
74
- break
75
- self.assertTrue(ignored, "ignored message found")
76
-
77
- def test_diff_meta_whitelist(self):
78
- if not os.path.exists("./jsons/az_ams_meta_before.json") \
79
- or not os.path.exists("./jsons/az_ams_meta_after.json"):
80
- return
81
- result = meta_diff(base_meta_file="./jsons/az_ams_meta_before.json",
82
- diff_meta_file="./jsons/az_ams_meta_after.json",
83
- output_type="text")
84
- self.assertEqual(result, [], "returned change isn't empty")
85
-
86
-
87
- if __name__ == '__main__':
88
- unittest.main()