azdev 0.1.87__tar.gz → 0.1.89__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 (104) hide show
  1. {azdev-0.1.87 → azdev-0.1.89}/HISTORY.rst +8 -0
  2. {azdev-0.1.87/azdev.egg-info → azdev-0.1.89}/PKG-INFO +9 -1
  3. {azdev-0.1.87 → azdev-0.1.89}/azdev/__init__.py +1 -1
  4. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/linter.py +93 -77
  5. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/regex.py +10 -0
  6. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/secret.py +34 -20
  7. {azdev-0.1.87 → azdev-0.1.89}/azdev/params.py +4 -0
  8. {azdev-0.1.87 → azdev-0.1.89/azdev.egg-info}/PKG-INFO +9 -1
  9. {azdev-0.1.87 → azdev-0.1.89}/LICENSE +0 -0
  10. {azdev-0.1.87 → azdev-0.1.89}/MANIFEST.in +0 -0
  11. {azdev-0.1.87 → azdev-0.1.89}/README.md +0 -0
  12. {azdev-0.1.87 → azdev-0.1.89}/README.rst +0 -0
  13. {azdev-0.1.87 → azdev-0.1.89}/azdev/__main__.py +0 -0
  14. {azdev-0.1.87 → azdev-0.1.89}/azdev/commands.py +0 -0
  15. {azdev-0.1.87 → azdev-0.1.89}/azdev/completer.py +0 -0
  16. {azdev-0.1.87 → azdev-0.1.89}/azdev/config/__init__.py +0 -0
  17. {azdev-0.1.87 → azdev-0.1.89}/azdev/config/cli.flake8 +0 -0
  18. {azdev-0.1.87 → azdev-0.1.89}/azdev/config/cli_pylintrc +0 -0
  19. {azdev-0.1.87 → azdev-0.1.89}/azdev/config/ext.flake8 +0 -0
  20. {azdev-0.1.87 → azdev-0.1.89}/azdev/config/ext_pylintrc +0 -0
  21. {azdev-0.1.87 → azdev-0.1.89}/azdev/help.py +0 -0
  22. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/HISTORY.rst +0 -0
  23. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/README.rst +0 -0
  24. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/_client_factory.py +0 -0
  25. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/_help.py +0 -0
  26. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/_params.py +0 -0
  27. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/_validators.py +0 -0
  28. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/azext_metadata.json +0 -0
  29. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/blank__init__.py +0 -0
  30. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/commands.py +0 -0
  31. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/custom.py +0 -0
  32. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/module__init__.py +0 -0
  33. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/pkg_declare__init__.py +0 -0
  34. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/setup.cfg +0 -0
  35. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/setup.py +0 -0
  36. {azdev-0.1.87 → azdev-0.1.89}/azdev/mod_templates/test_service_scenario.py +0 -0
  37. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/__init__.py +0 -0
  38. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/breaking_change/__init__.py +0 -0
  39. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/breaking_change/markdown_template.jinja2 +0 -0
  40. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/__init__.py +0 -0
  41. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/_macros.j2 +0 -0
  42. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/cmdcov.py +0 -0
  43. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/component.css +0 -0
  44. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/component.js +0 -0
  45. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/favicon.ico +0 -0
  46. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/index.j2 +0 -0
  47. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/index2.j2 +0 -0
  48. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/cmdcov/module.j2 +0 -0
  49. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/code_gen.py +0 -0
  50. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/command_change/__init__.py +0 -0
  51. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/command_change/custom.py +0 -0
  52. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/command_change/util.py +0 -0
  53. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/constant.py +0 -0
  54. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/extensions/__init__.py +0 -0
  55. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/extensions/util.py +0 -0
  56. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/extensions/version_upgrade.py +0 -0
  57. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/help/__init__.py +0 -0
  58. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/help/refdoc/__init__.py +0 -0
  59. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/help/refdoc/conf.py +0 -0
  60. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/legal.py +0 -0
  61. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/__init__.py +0 -0
  62. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
  63. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
  64. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rule_decorators.py +0 -0
  65. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/__init__.py +0 -0
  66. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
  67. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
  68. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/command_group_rules.py +0 -0
  69. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/command_rules.py +0 -0
  70. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/help_rules.py +0 -0
  71. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
  72. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/rules/parameter_rules.py +0 -0
  73. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/linter/util.py +0 -0
  74. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/performance.py +0 -0
  75. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/pypi.py +0 -0
  76. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/python_sdk.py +0 -0
  77. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/resource.py +0 -0
  78. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/setup.py +0 -0
  79. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/statistics/__init__.py +0 -0
  80. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/statistics/util.py +0 -0
  81. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/style.py +0 -0
  82. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/testtool/__init__.py +0 -0
  83. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/testtool/incremental_strategy.py +0 -0
  84. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/testtool/profile_context.py +0 -0
  85. {azdev-0.1.87 → azdev-0.1.89}/azdev/operations/testtool/pytest_runner.py +0 -0
  86. {azdev-0.1.87 → azdev-0.1.89}/azdev/transformers.py +0 -0
  87. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/__init__.py +0 -0
  88. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/command.py +0 -0
  89. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/config.py +0 -0
  90. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/const.py +0 -0
  91. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/display.py +0 -0
  92. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/git_util.py +0 -0
  93. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/path.py +0 -0
  94. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/pypi.py +0 -0
  95. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/testing.py +0 -0
  96. {azdev-0.1.87 → azdev-0.1.89}/azdev/utilities/tools.py +0 -0
  97. {azdev-0.1.87 → azdev-0.1.89}/azdev.egg-info/SOURCES.txt +0 -0
  98. {azdev-0.1.87 → azdev-0.1.89}/azdev.egg-info/dependency_links.txt +0 -0
  99. {azdev-0.1.87 → azdev-0.1.89}/azdev.egg-info/entry_points.txt +0 -0
  100. {azdev-0.1.87 → azdev-0.1.89}/azdev.egg-info/requires.txt +0 -0
  101. {azdev-0.1.87 → azdev-0.1.89}/azdev.egg-info/top_level.txt +0 -0
  102. {azdev-0.1.87 → azdev-0.1.89}/pyproject.toml +0 -0
  103. {azdev-0.1.87 → azdev-0.1.89}/setup.cfg +0 -0
  104. {azdev-0.1.87 → azdev-0.1.89}/setup.py +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  Release History
4
4
  ===============
5
+ 0.1.89
6
+ ++++++
7
+ * `azdev scan/mask`: Add `--continue-on-failure` support
8
+
9
+ 0.1.88
10
+ ++++++
11
+ * `azdev cmdcov`: Fix incorrect detection of code changes as new commands
12
+
5
13
  0.1.87
6
14
  ++++++
7
15
  * `azdev linter`: Fix repo path failed when `detect_new_command`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azdev
3
- Version: 0.1.87
3
+ Version: 0.1.89
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
5
  Home-page: https://github.com/Azure/azure-cli-dev-tools
6
6
  Author: Microsoft Corporation
@@ -148,6 +148,14 @@ License
148
148
 
149
149
  Release History
150
150
  ===============
151
+ 0.1.89
152
+ ++++++
153
+ * `azdev scan/mask`: Add `--continue-on-failure` support
154
+
155
+ 0.1.88
156
+ ++++++
157
+ * `azdev cmdcov`: Fix incorrect detection of code changes as new commands
158
+
151
159
  0.1.87
152
160
  ++++++
153
161
  * `azdev linter`: Fix repo path failed when `detect_new_command`.
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __VERSION__ = '0.1.87'
7
+ __VERSION__ = '0.1.89'
@@ -11,6 +11,7 @@ import inspect
11
11
  import os
12
12
  import re
13
13
  from pkgutil import iter_modules
14
+ from typing import List, Tuple
14
15
  import yaml
15
16
  from knack.log import get_logger
16
17
 
@@ -19,6 +20,7 @@ from azdev.operations.regex import (
19
20
  search_argument,
20
21
  search_argument_context,
21
22
  search_command,
23
+ search_deleted_command,
22
24
  search_command_group)
23
25
  from azdev.utilities import diff_branches_detail, diff_branch_file_patch
24
26
  from azdev.utilities.path import get_cli_repo_path, get_ext_repo_paths
@@ -222,86 +224,100 @@ class Linter: # pylint: disable=too-many-public-methods, too-many-instance-attr
222
224
  all_tested_command = self._detect_tested_command(diff_index)
223
225
  return self._run_parameter_test_coverage(parameters, all_tested_command)
224
226
 
225
- # pylint: disable=too-many-locals, too-many-nested-blocks, too-many-branches, too-many-statements
226
- def _detect_new_command(self, diff_index):
227
- """
228
- exclude_comands: List[str]
229
- exclude_parameters: List[tuple[str, str]]
230
- commands: List[str]
231
- parameters: List[str, List[str]]
232
- """
227
+ def _get_exclusions(self):
228
+ _exclude_commands = set()
229
+ _exclude_parameters = set()
230
+ for command, details in self.exclusions.items():
231
+ if 'parameters' in details:
232
+ for param, rules in details['parameters'].items():
233
+ if 'missing_parameter_test_coverage' in rules['rule_exclusions']:
234
+ _exclude_parameters.add((command, param))
235
+ if 'rule_exclusions' in details and 'missing_command_test_coverage' in details['rule_exclusions']:
236
+ _exclude_commands.add(command)
237
+ _logger.debug('exclude_parameters: %s', _exclude_parameters)
238
+ _logger.debug('exclude_comands: %s', _exclude_commands)
239
+ return _exclude_commands, _exclude_parameters
240
+
241
+ def _split_path(self, path: str):
242
+ parts = path.rsplit('/', maxsplit=1)
243
+ return parts if len(parts) == 2 else ('', parts[0])
244
+
245
+ def _read_blob_lines(self, blob):
246
+ return blob.data_stream.read().decode("utf-8").splitlines(True) if blob else []
247
+
248
+ def _get_line_number(self, lines: List[str], row_num: int, pattern: str):
249
+ offset = -1
250
+ while row_num > 0:
251
+ row_num -= 1
252
+ match = re.findall(pattern, lines[row_num])
253
+ offset += 1
254
+ if match:
255
+ return int(match[0]) + offset
256
+ return -1
257
+
258
+ def _extract_parameters(self, lines, current_lines, _exclude_commands, _exclude_parameters, parameters):
259
+ for row_num, line in enumerate(lines):
260
+ params, param_name = search_argument(line)
261
+ if params:
262
+ idx = self._get_line_number(lines, row_num, r'--- (\d+),(?:\d+) ----')
263
+ commands = search_argument_context(idx, current_lines)
264
+ for cmd in commands:
265
+ if cmd not in _exclude_commands and (cmd, param_name) not in _exclude_parameters:
266
+ parameters.append((cmd, params))
267
+ _logger.debug('Detected parameter: [%s, %s]', cmd, params)
268
+ return parameters
269
+
270
+ def _extract_commands(self, lines, original_lines, current_lines, added_commands,
271
+ deleted_commands, _exclude_commands, yellow_color):
272
+ for row_num, line in enumerate(lines):
273
+ added_command = search_command(line)
274
+ deleted_command = search_deleted_command(line)
275
+
276
+ if added_command:
277
+ idx = self._get_line_number(lines, row_num, r'--- (\d+),(?:\d+) ----')
278
+ cmd = search_command_group(idx, current_lines, added_command)
279
+ if cmd:
280
+ if cmd in _exclude_commands:
281
+ _logger.warning('%sCommand %s not tested and excluded in linter_exclusions.yml',
282
+ yellow_color, cmd)
283
+ else:
284
+ added_commands.add(cmd)
285
+
286
+ elif deleted_command:
287
+ idx = self._get_line_number(lines, row_num, r'\*\*\* (\d+),(?:\d+) \*\*\*\*')
288
+ cmd = search_command_group(idx, original_lines, deleted_command)
289
+ if cmd:
290
+ deleted_commands.add(cmd)
291
+ return added_commands, deleted_commands
292
+
293
+ def _detect_new_command(self, diff_index: List) -> Tuple[List[str], List[Tuple[str, str]]]:
233
294
  YELLOW = '\x1b[33m'
234
- parameters = []
235
- commands = []
236
- lines = []
237
- exclude_comands = []
238
- exclude_parameters = []
239
- for c, v in self.exclusions.items():
240
- if 'parameters' in v:
241
- for p, r in v['parameters'].items():
242
- if 'missing_parameter_test_coverage' in r['rule_exclusions']:
243
- exclude_parameters.append((c, p))
244
- if 'rule_exclusions' in v:
245
- if 'missing_command_test_coverage' in v['rule_exclusions']:
246
- exclude_comands.append(c)
247
- _logger.debug('exclude_parameters: %s', exclude_parameters)
248
- _logger.debug('exclude_comands: %s', exclude_comands)
295
+ _exclude_commands, _exclude_parameters = self._get_exclusions()
296
+ added_commands, deleted_commands, parameters = set(), set(), []
249
297
 
250
298
  for diff in diff_index:
251
- filename = diff.a_path.split('/')[-1]
252
- if 'params' in filename or 'commands' in filename:
253
- lines = list(
254
- context_diff(diff.a_blob.data_stream.read().decode("utf-8").splitlines(True) if diff.a_blob else [],
255
- diff.b_blob.data_stream.read().decode("utf-8").splitlines(True) if diff.b_blob else [],
256
- 'Original', 'Current'))
257
- for row_num, line in enumerate(lines):
258
- if 'params.py' in filename:
259
- params, param_name = search_argument(line)
260
- if params:
261
- offset = -1
262
- while row_num > 0:
263
- row_num -= 1
264
- # Match row num '--- 156,163 ----'
265
- sub_pattern = r'--- (\d{0,}),(?:\d{0,}) ----'
266
- idx = re.findall(sub_pattern, lines[row_num])
267
- offset += 1
268
- if idx:
269
- idx = int(idx[0]) + offset
270
- break
271
- with open(os.path.join(self.git_repo, diff.a_path), encoding='utf-8') as f:
272
- param_lines = f.readlines()
273
- cmds = search_argument_context(idx, param_lines)
274
- for cmd in cmds:
275
- if cmd not in exclude_comands and \
276
- not list(filter(lambda x, c=cmd, p=param_name: c in x[0] and p in x[1], exclude_parameters)): # pylint: disable=line-too-long
277
- parameters.append([cmd, params])
278
- else:
279
- print('%sCommand [%s, %s] not test and exclude in linter_exclusions.yml' % (
280
- YELLOW, cmd, params))
281
-
282
- if 'commands.py' in filename:
283
- command = search_command(line)
284
- if command:
285
- offset = -1
286
- while row_num > 0:
287
- row_num -= 1
288
- # Match row num '--- 156,163 ----'
289
- sub_pattern = r'--- (\d{0,}),(?:\d{0,}) ----'
290
- idx = re.findall(sub_pattern, lines[row_num])
291
- offset += 1
292
- if idx:
293
- idx = int(idx[0]) + offset
294
- break
295
- with open(os.path.join(self.git_repo, diff.a_path), encoding='utf-8') as f:
296
- cmd_lines = f.readlines()
297
- cmd = search_command_group(idx, cmd_lines, command)
298
- if cmd:
299
- if cmd in exclude_comands:
300
- print('%sCommand %s not test and exclude in linter_exclusions.yml' % (YELLOW, cmd))
301
- else:
302
- commands.append(cmd)
303
- _logger.debug('New add parameters: %s', parameters)
304
- _logger.debug('New add commands: %s', commands)
299
+ _, filename = self._split_path(diff.a_path)
300
+ if not any(key in filename for key in ('params', 'commands')):
301
+ continue
302
+
303
+ original_lines = self._read_blob_lines(diff.a_blob)
304
+ current_lines = self._read_blob_lines(diff.b_blob)
305
+ lines = list(context_diff(original_lines, current_lines, 'Original', 'Current'))
306
+
307
+ if 'params.py' in filename:
308
+ parameters = self._extract_parameters(lines, current_lines, _exclude_commands,
309
+ _exclude_parameters, parameters)
310
+
311
+ if 'commands.py' in filename:
312
+ added_commands, deleted_commands = self._extract_commands(lines, original_lines, current_lines,
313
+ added_commands, deleted_commands,
314
+ _exclude_commands, YELLOW)
315
+
316
+ commands = list(added_commands - deleted_commands)
317
+ _logger.debug('New parameters: %s', parameters)
318
+ _logger.debug('Added commands: %s', added_commands)
319
+ _logger.debug('Deleted commands: %s', deleted_commands)
320
+ _logger.debug('Final commands: %s', commands)
305
321
  return commands, parameters
306
322
 
307
323
  def _detect_tested_command(self, diff_index):
@@ -162,3 +162,13 @@ def search_command(line):
162
162
  if ref:
163
163
  command = ref[0].split(',')[0].strip("'")
164
164
  return command
165
+
166
+
167
+ def search_deleted_command(line):
168
+ command = ''
169
+ # Match `- g.*command(xxx)`
170
+ pattern = r'\-\s+g.(?:\w+)?command\((.*)\)'
171
+ ref = re.findall(pattern, line)
172
+ if ref:
173
+ command = ref[0].split(',')[0].strip("'")
174
+ return command
@@ -146,7 +146,8 @@ def _scan_secrets_for_string(data, confidence_level=None, custom_pattern=None):
146
146
  def scan_secrets(file_path=None, directory_path=None, recursive=False,
147
147
  include_pattern=None, exclude_pattern=None, data=None,
148
148
  save_scan_result=None, scan_result_path=None,
149
- confidence_level=None, custom_pattern=None):
149
+ confidence_level=None, custom_pattern=None,
150
+ continue_on_failure=None):
150
151
  _validate_data_path(file_path=file_path, directory_path=directory_path,
151
152
  include_pattern=include_pattern, exclude_pattern=exclude_pattern, data=data)
152
153
  target_files = []
@@ -165,15 +166,21 @@ def scan_secrets(file_path=None, directory_path=None, recursive=False,
165
166
  scan_results['raw_data'] = secrets
166
167
  elif target_files:
167
168
  for target_file in target_files:
168
- logger.debug('start scanning secrets for %s', target_file)
169
- with open(target_file, encoding='utf8') as f:
170
- data = f.read()
171
- if not data:
172
- continue
173
- secrets = _scan_secrets_for_string(data, confidence_level, custom_pattern)
174
- logger.debug('%d secrets found for %s', len(secrets), target_file)
175
- if secrets:
176
- scan_results[target_file] = secrets
169
+ try:
170
+ logger.debug('start scanning secrets for %s', target_file)
171
+ with open(target_file, encoding='utf8') as f:
172
+ data = f.read()
173
+ if not data:
174
+ continue
175
+ secrets = _scan_secrets_for_string(data, confidence_level, custom_pattern)
176
+ logger.debug('%d secrets found for %s', len(secrets), target_file)
177
+ if secrets:
178
+ scan_results[target_file] = secrets
179
+ except Exception as ex: # pylint: disable=broad-exception-caught
180
+ if continue_on_failure:
181
+ logger.warning("Error handling file %s, exception %s", target_file, str(ex))
182
+ else:
183
+ raise ex
177
184
 
178
185
  if scan_result_path:
179
186
  save_scan_result = True
@@ -244,7 +251,7 @@ def _mask_secret_for_string(data, secret, redaction_type=None):
244
251
  def mask_secrets(file_path=None, directory_path=None, recursive=False,
245
252
  include_pattern=None, exclude_pattern=None, data=None,
246
253
  save_scan_result=None, scan_result_path=None,
247
- confidence_level=None, custom_pattern=None,
254
+ confidence_level=None, custom_pattern=None, continue_on_failure=None,
248
255
  saved_scan_result_path=None, redaction_type='FIXED_VALUE', yes=None):
249
256
  scan_results = {}
250
257
  if saved_scan_result_path:
@@ -259,7 +266,8 @@ def mask_secrets(file_path=None, directory_path=None, recursive=False,
259
266
  scan_response = scan_secrets(file_path=file_path, directory_path=directory_path, recursive=recursive,
260
267
  include_pattern=include_pattern, exclude_pattern=exclude_pattern, data=data,
261
268
  save_scan_result=save_scan_result, scan_result_path=scan_result_path,
262
- confidence_level=confidence_level, custom_pattern=custom_pattern)
269
+ confidence_level=confidence_level, custom_pattern=custom_pattern,
270
+ continue_on_failure=continue_on_failure)
263
271
  if save_scan_result and scan_response['scan_result_path']:
264
272
  with open(scan_response['scan_result_path'], encoding='utf8') as f:
265
273
  scan_results = json.load(f)
@@ -291,13 +299,19 @@ def mask_secrets(file_path=None, directory_path=None, recursive=False,
291
299
  return mask_result
292
300
 
293
301
  for scan_file_path, secrets in scan_results.items():
294
- with open(scan_file_path, 'r', encoding='utf8') as f:
295
- content = f.read()
296
- if not content:
297
- continue
298
- for secret in secrets:
299
- content = _mask_secret_for_string(content, secret, redaction_type)
300
- with open(scan_file_path, 'w', encoding='utf8') as f:
301
- f.write(content)
302
+ try:
303
+ with open(scan_file_path, 'r', encoding='utf8') as f:
304
+ content = f.read()
305
+ if not content:
306
+ continue
307
+ for secret in secrets:
308
+ content = _mask_secret_for_string(content, secret, redaction_type)
309
+ with open(scan_file_path, 'w', encoding='utf8') as f:
310
+ f.write(content)
311
+ except Exception as ex: # pylint: disable=broad-exception-caught
312
+ if continue_on_failure:
313
+ logger.warning("Error handling file %s, exception %s", scan_file_path, str(ex))
314
+ else:
315
+ raise ex
302
316
  mask_result['mask'] = True
303
317
  return mask_result
@@ -132,6 +132,10 @@ def load_arguments(self, _):
132
132
  c.argument('custom_pattern',
133
133
  help='Additional patterns you want to apply or built-in patterns you want to exclude '
134
134
  'for scanning. Can be json string or path to the json file.')
135
+ c.argument('continue_on_failure', action='store_true',
136
+ help='If not, the operation will terminate quickly on encountering file operation errors. '
137
+ 'If true, the operation will warning the error for specific file and proceed with other files. '
138
+ 'If not set the default value is false.')
135
139
 
136
140
  with ArgumentsContext(self, 'mask') as c:
137
141
  c.argument('yes', options_list=['--yes', '-y'], action='store_true', help='Answer "yes" to all prompts.')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azdev
3
- Version: 0.1.87
3
+ Version: 0.1.89
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
5
  Home-page: https://github.com/Azure/azure-cli-dev-tools
6
6
  Author: Microsoft Corporation
@@ -148,6 +148,14 @@ License
148
148
 
149
149
  Release History
150
150
  ===============
151
+ 0.1.89
152
+ ++++++
153
+ * `azdev scan/mask`: Add `--continue-on-failure` support
154
+
155
+ 0.1.88
156
+ ++++++
157
+ * `azdev cmdcov`: Fix incorrect detection of code changes as new commands
158
+
151
159
  0.1.87
152
160
  ++++++
153
161
  * `azdev linter`: Fix repo path failed when `detect_new_command`.
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes