def check_cli(cmd): from azure.cli.core.file_util import ( create_invoker_and_load_cmds_and_args, get_all_help) exceptions = {} print('Running CLI self-test.\n') print('Loading all commands and arguments...') try: create_invoker_and_load_cmds_and_args(cmd.cli_ctx) print('Commands loaded OK.\n') except Exception as ex: # pylint: disable=broad-except exceptions['load_commands'] = ex logger.error('Error occurred loading commands!\n') print('Retrieving all help...') try: get_all_help(cmd.cli_ctx) print('Help loaded OK.\n') except Exception as ex: # pylint: disable=broad-except exceptions['load_help'] = ex logger.error('Error occurred loading help!\n') if not exceptions: print('CLI self-test completed: OK') else: raise CLIError(exceptions)
def check_cli(cmd): from azure.cli.core.file_util import ( create_invoker_and_load_cmds_and_args, get_all_help) exceptions = {} print('Running CLI self-test.\n') print('Loading all commands and arguments...') try: create_invoker_and_load_cmds_and_args(cmd.cli_ctx) print('Commands loaded OK.\n') except Exception as ex: # pylint: disable=broad-except exceptions['load_commands'] = ex logger.error('Error occurred loading commands!\n') print('Retrieving all help...') try: get_all_help(cmd.cli_ctx, skip=False) print('Help loaded OK.\n') except Exception as ex: # pylint: disable=broad-except exceptions['load_help'] = ex logger.error('Error occurred loading help!\n') if not exceptions: print('CLI self-test completed: OK') else: raise CLIError(exceptions)
def test_load_from_help_yaml(self, mocked_load, mocked_pkg_util): # setup help.py and help.yaml help. self.set_help_py() yaml_path = self.set_help_yaml() create_invoker_and_load_cmds_and_args(self.test_cli) # mock logic in core._help_loaders for retrieving yaml file from loader path. expected_arg = self.test_cli.invocation.commands_loader.cmd_to_loader_map['test alpha'][0].__class__ with mock.patch('inspect.getfile', side_effect=get_mocked_inspect_getfile(expected_arg, yaml_path)): group_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test"), None) command_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test alpha"), None) # pylint: disable=line-too-long # Test that group and command help are successfully displayed. with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "-h"]) with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "alpha", "-h"]) # Test group help self.assertIsNotNone(group_help_obj) self.assertEqual(group_help_obj.short_summary, "Group yaml summary.") self.assertEqual(group_help_obj.long_summary, "Group yaml description. A.K.A long description.") self.assertEqual(group_help_obj.links[0], {"title": "Azure Test Docs", "url": "https://docs.microsoft.com/en-us/azure/test"}) self.assertEqual(group_help_obj.links[1], {"url": "https://aka.ms/just-a-url"}) # Test command help self.assertIsNotNone(command_help_obj) self.assertEqual(command_help_obj.short_summary, "Command yaml summary.") self.assertEqual(command_help_obj.long_summary, "Command yaml description. A.K.A long description.") self.assertEqual(command_help_obj.links[0], {"title": "Azure Test Alpha Docs", "url": "https://docs.microsoft.com/en-us/azure/test/alpha"}) self.assertEqual(command_help_obj.links[1], {"url": "https://aka.ms/just-a-long-url"}) # test that parameters and help are loaded from command function docstring, argument registry help and help.yaml obj_param_dict = {param.name: param for param in command_help_obj.parameters} param_name_set = {"--arg1 -a", "--arg2 -b", "--arg3", "ARG4"} self.assertTrue(set(obj_param_dict.keys()).issuperset(param_name_set)) self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg3"].short_summary, "Arg3's docstring help text.") self.assertEqual(obj_param_dict["ARG4"].short_summary, "Arg4's summary, yaml. Positional arg, not required.") self.assertEqual(obj_param_dict["--arg2 -b"].short_summary, "Arg 2's summary.") self.assertEqual(obj_param_dict["--arg2 -b"].long_summary, "A true description of this parameter.") self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[0], {"string": "Number range: -5.0 to 5.0"}) self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[1]['link'], {"url": "https://www.foo.com", "title": "foo"}) self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[2]['link'], {"command": "az test show", "title": "Show test details"}) self.assertEqual(command_help_obj.examples[0].short_summary, "A simple example") self.assertEqual(command_help_obj.examples[0].long_summary, "More detail on the simple example.") self.assertEqual(command_help_obj.examples[0].command, "az test alpha --arg1 apple --arg2 ball --arg3 cat") self.assertEqual(command_help_obj.examples[0].supported_profiles, "2018-03-01-hybrid, latest") self.assertEqual(command_help_obj.examples[0].unsupported_profiles, None) self.assertEqual(command_help_obj.examples[1].supported_profiles, None) self.assertEqual(command_help_obj.examples[1].unsupported_profiles, "2017-03-09-profile")
def test_load_from_help_py(self, mocked_load, mocked_pkg_util): self.set_help_py() create_invoker_and_load_cmds_and_args(self.test_cli) group_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test"), None) command_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test alpha"), None) # Test that group and command help are successfully displayed. with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "-h"]) with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "alpha", "-h"]) # Test group help self.assertIsNotNone(group_help_obj) self.assertEqual(group_help_obj.short_summary, "Foo Bar Group.") self.assertEqual(group_help_obj.long_summary, "Foo Bar Baz Group is a fun group.") # Test command help self.assertIsNotNone(command_help_obj) self.assertEqual(command_help_obj.short_summary, "Foo Bar Command.") self.assertEqual(command_help_obj.long_summary, "Foo Bar Baz Command is a fun command.") # test that parameters and help are loaded from command function docstring, argument registry help and help.py obj_param_dict = {param.name: param for param in command_help_obj.parameters} param_name_set = {"--arg1 -a", "--arg2 -b", "--arg3", "ARG4"} self.assertTrue(set(obj_param_dict.keys()).issuperset(param_name_set)) self.assertEqual(obj_param_dict["--arg3"].short_summary, "Arg3's docstring help text.") self.assertEqual(obj_param_dict["ARG4"].short_summary, "Positional parameter. Not required.") self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg1 -a"].value_sources[0]['link']['command'], "az foo bar") self.assertEqual(obj_param_dict["--arg1 -a"].value_sources[1]['link']['command'], "az bar baz") if self.test_cli.cloud.profile in ['2018-03-01-hybrid', 'latest']: self.assertEqual(command_help_obj.examples[0].short_summary, "Alpha Example") self.assertEqual(command_help_obj.examples[0].command, "az test alpha --arg1 a --arg2 b --arg3 c") self.assertEqual(command_help_obj.examples[0].supported_profiles, "2018-03-01-hybrid, latest") self.assertEqual(command_help_obj.examples[0].unsupported_profiles, None) self.assertEqual(command_help_obj.examples[1].supported_profiles, None) self.assertEqual(command_help_obj.examples[1].unsupported_profiles, "2017-03-09-profile") if self.test_cli.cloud.profile == '2019-03-01-hybrid': self.assertEqual(len(command_help_obj.examples), 1) self.assertEqual(command_help_obj.examples[0].short_summary, "A simple example unsupported on latest") self.assertEqual(command_help_obj.examples[0].command, "az test alpha --arg1 a --arg2 b") self.assertEqual(command_help_obj.examples[0].unsupported_profiles, '2017-03-09-profile') if self.test_cli.cloud.profile == '2017-03-09-profile': self.assertEqual(len(command_help_obj.examples), 0)
def main(args): from azure.cli.core import get_default_cli from azure.cli.core.file_util import get_all_help, create_invoker_and_load_cmds_and_args print('Initializing linter with command table and help files...') # setup CLI to enable command loader az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) command_table = az_cli.invocation.commands_loader.command_table # format loaded help loaded_help = {data.command: data for data in loaded_help if data.command} # load yaml help help_file_entries = {} for entry_name, help_yaml in helps.items(): help_entry = yaml.load(help_yaml) help_file_entries[entry_name] = help_entry if not args.rule_types_to_run: args.rule_types_to_run = [ 'params', 'commands', 'command_groups', 'help_entries' ] # find rule exclusions and pass to linter manager from ..utilities.path import get_command_modules_paths exclusions = {} command_modules_paths = get_command_modules_paths() for _, path in command_modules_paths: exclusion_path = os.path.join(path, 'linter_exclusions.yml') if os.path.isfile(exclusion_path): mod_exclusions = yaml.load(open(exclusion_path)) exclusions.update(mod_exclusions) # only run linter on modules specified if args.modules: from .util import include_mods command_table, help_file_entries = include_mods( command_table, help_file_entries, args.modules) # Instantiate and run Linter linter_manager = LinterManager(command_table=command_table, help_file_entries=help_file_entries, loaded_help=loaded_help, exclusions=exclusions, rule_inclusions=args.rules) exit_code = linter_manager.run(run_params='params' in args.rule_types_to_run, run_commands='commands' in args.rule_types_to_run, run_command_groups='command_groups' in args.rule_types_to_run, run_help_files_entries='help_entries' in args.rule_types_to_run, ci=args.ci) sys.exit(exit_code)
def main(args): from azure.cli.core import get_default_cli from azure.cli.core.file_util import get_all_help, create_invoker_and_load_cmds_and_args print('Initializing linter with command table and help files...') # setup CLI to enable command loader az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) command_loader = az_cli.invocation.commands_loader # format loaded help loaded_help = {data.command: data for data in loaded_help if data.command} # load yaml help help_file_entries = {} for entry_name, help_yaml in helps.items(): help_entry = yaml.load(help_yaml) help_file_entries[entry_name] = help_entry if not args.rule_types_to_run: args.rule_types_to_run = ['params', 'commands', 'command_groups', 'help_entries'] # find rule exclusions and pass to linter manager from ..utilities.path import get_command_modules_paths, get_extensions_paths exclusions = {} command_modules_paths = get_command_modules_paths() extension_paths = get_extensions_paths() for gen in (command_modules_paths, extension_paths): for _, path in gen: exclusion_path = os.path.join(path, 'linter_exclusions.yml') if os.path.isfile(exclusion_path): mod_exclusions = yaml.load(open(exclusion_path)) exclusions.update(mod_exclusions) # only run linter on modules and extensions specified if args.modules or args.extensions: from .util import include_commands command_loader, help_file_entries = include_commands( command_loader, help_file_entries, module_inclusions=args.modules, extensions=args.extensions) # Instantiate and run Linter linter_manager = LinterManager(command_loader=command_loader, help_file_entries=help_file_entries, loaded_help=loaded_help, exclusions=exclusions, rule_inclusions=args.rules) exit_code = linter_manager.run(run_params='params' in args.rule_types_to_run, run_commands='commands' in args.rule_types_to_run, run_command_groups='command_groups' in args.rule_types_to_run, run_help_files_entries='help_entries' in args.rule_types_to_run, ci=args.ci) sys.exit(exit_code)
def test_load_from_help_py(self, mocked_load, mocked_pkg_util): self.set_help_py() create_invoker_and_load_cmds_and_args(self.test_cli) group_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test"), None) command_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test alpha"), None) # Test that group and command help are successfully displayed. with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "-h"]) with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "alpha", "-h"]) # Test group help self.assertIsNotNone(group_help_obj) self.assertEqual(group_help_obj.short_summary, "Foo Bar Group.") self.assertEqual(group_help_obj.long_summary, "Foo Bar Baz Group is a fun group.") # Test command help self.assertIsNotNone(command_help_obj) self.assertEqual(command_help_obj.short_summary, "Foo Bar Command.") self.assertEqual(command_help_obj.long_summary, "Foo Bar Baz Command is a fun command.") # test that parameters and help are loaded from command function docstring, argument registry help and help.py obj_param_dict = {param.name: param for param in command_help_obj.parameters} param_name_set = {"--arg1 -a", "--arg2 -b", "--arg3", "ARG4"} self.assertTrue(set(obj_param_dict.keys()).issuperset(param_name_set)) self.assertEqual(obj_param_dict["--arg3"].short_summary, "Arg3's docstring help text.") self.assertEqual(obj_param_dict["ARG4"].short_summary, "Positional parameter. Not required.") self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg1 -a"].value_sources[0]['link']['command'], "az foo bar") self.assertEqual(obj_param_dict["--arg1 -a"].value_sources[1]['link']['command'], "az bar baz") self.assertEqual(command_help_obj.examples[0].short_summary, "Alpha Example") self.assertEqual(command_help_obj.examples[0].command, "az test alpha --arg1 a --arg2 b --arg3 c") self.assertEqual(command_help_obj.examples[0].supported_profiles, "2018-03-01-hybrid, latest") self.assertEqual(command_help_obj.examples[0].unsupported_profiles, None) self.assertEqual(command_help_obj.examples[1].supported_profiles, None) self.assertEqual(command_help_obj.examples[1].unsupported_profiles, "2017-03-09-profile")
def verify_load_all(_): from azure.cli.core import get_default_cli from azure.cli.core.file_util import get_all_help, create_invoker_and_load_cmds_and_args print('Loading all commands, arguments, and help...') # setup CLI to enable command loader az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) print('Everything loaded successfully.')
def verify_load_all(_): from azure.cli.core import get_default_cli, EVENT_FAILED_EXTENSION_LOAD from azure.cli.core.file_util import get_all_help, create_invoker_and_load_cmds_and_args print('Loading all commands, arguments, and help...') # setup CLI to enable command loader and register event az_cli = get_default_cli() az_cli.register_event(EVENT_FAILED_EXTENSION_LOAD, extension_failed_load_handler) # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) # verify each installed extension is properly loaded if not FAILED_TO_LOAD or set(FAILED_TO_LOAD).issubset(set(EXTENSION_FAILURE_EXCLUSIONS)): print('Everything loaded successfully.') else: raise Exception('Exceptions failed to load: {}'.format(', '.join(FAILED_TO_LOAD)))
def main(): az_cli = AzCli(cli_name='az', commands_loader_cls=MainCommandsLoader, invocation_cls=AzCliCommandInvoker, parser_cls=AzCliCommandParser, help_cls=AzCliHelp) create_invoker_and_load_cmds_and_args(az_cli) help_files = get_all_help(az_cli) high_command_set = set() for help_file in help_files: if help_file.command: high_command_set.add(help_file.command.split()[0]) print('high_command_set:') print(high_command_set) # Load and check service_name.json with open('src/azure-cli/service_name.json') as f: service_names = json.load(f) print('Verifying src/azure-cli/service_name.json') service_name_map = {} for service_name in service_names: command = service_name['Command'] service = service_name['AzureServiceName'] if not command.startswith('az '): raise Exception('{} does not start with az!'.format(command)) if not service: raise Exception('AzureServiceName of {} is empty!'.format(command)) service_name_map[command[3:]] = service print('service_name_map:') print(service_name_map) # Check existence in service_name.json for high_command in high_command_set: if high_command not in service_name_map: raise Exception( 'No entry of {} in service_name.json. Please add one to the file.' .format(high_command))
def test_load_from_help_json(self, mocked_load, mocked_pkg_util, mocked_getmembers): # setup help.py, help.yaml and help.json self.set_help_py() path = self.set_help_yaml( ) # either (yaml or json) path should work. As both files are in the same temp dir. self.set_help_json() create_invoker_and_load_cmds_and_args(self.test_cli) # mock logic in core._help_loaders for retrieving yaml file from loader path. expected_arg = self.test_cli.invocation.commands_loader.cmd_to_loader_map[ 'test alpha'][0].__class__ with mock.patch('inspect.getfile', side_effect=get_mocked_inspect_getfile( expected_arg, path)): group_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test"), None) command_help_obj = next((help for help in get_all_help(self.test_cli) if help.command == "test alpha"), None) # Test that group and command help are successfully displayed. with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "-h"]) with self.assertRaises(SystemExit): self.test_cli.invoke(["test", "alpha", "-h"]) # Test group help self.assertIsNotNone(group_help_obj) self.assertEqual(group_help_obj.short_summary, "Group json summary.") self.assertEqual(group_help_obj.long_summary, "Group json description. A.K.A long description.") self.assertEqual( group_help_obj.links[0], { "title": "Azure Json Test Docs", "url": "https://docs.microsoft.com/azure/test" }) self.assertEqual(group_help_obj.links[1], {"url": "https://aka.ms/just-a-url"}) # Test command help self.assertIsNotNone(command_help_obj) self.assertEqual(command_help_obj.short_summary, "Command json summary.") self.assertEqual(command_help_obj.long_summary, "Command json description. A.K.A long description.") self.assertEqual( command_help_obj.links[0], { "title": "Azure Json Test Alpha Docs", "url": "https://docs.microsoft.com/azure/test/alpha" }) self.assertEqual(command_help_obj.links[1], {"url": "https://aka.ms/just-a-long-url"}) # test that parameters and help are loaded from command function docstring, argument registry help and help.yaml obj_param_dict = { param.name: param for param in command_help_obj.parameters } param_name_set = {"--arg1 -a", "--arg2 -b", "--arg3", "ARG4"} self.assertTrue(set(obj_param_dict.keys()).issuperset(param_name_set)) self.assertEqual(obj_param_dict["--arg3"].short_summary, "Arg 3's json summary.") self.assertEqual(obj_param_dict["--arg3"].long_summary, "A truly true description of this parameter.") self.assertEqual(obj_param_dict["--arg3"].value_sources[0], {"string": "Number range: 0 to 10"}) self.assertEqual(obj_param_dict["--arg3"].value_sources[1]['link'], { "url": "https://www.foo-json.com", "title": "foo-json" }) self.assertEqual(obj_param_dict["--arg3"].value_sources[2]['link'], { "command": "az test show", "title": "Show test details. Json file" }) if self.test_cli.cloud.profile in ['2018-03-01-hybrid', 'latest']: self.assertEqual(command_help_obj.examples[0].short_summary, "A simple example from json") self.assertEqual(command_help_obj.examples[0].long_summary, "More detail on the simple example.") self.assertEqual( command_help_obj.examples[0].command, "az test alpha --arg1 alpha --arg2 beta --arg3 chi") self.assertEqual(command_help_obj.examples[0].supported_profiles, "2018-03-01-hybrid, latest") if self.test_cli.cloud.profile == '2019-03-01-hybrid': # only supported example here self.assertEqual(len(command_help_obj.examples), 0) if self.test_cli.cloud.profile == '2017-03-09-profile': self.assertEqual(len(command_help_obj.examples), 0) # validate other parameters, which have help from help.py and help.yamls self.assertEqual(obj_param_dict["--arg1 -a"].short_summary, "A short summary.") self.assertEqual(obj_param_dict["--arg2 -b"].short_summary, "Arg 2's summary.") self.assertEqual( obj_param_dict["ARG4"].short_summary, "Arg4's summary, yaml. Positional arg, not required.") # arg2's help from help.yaml still preserved. self.assertEqual(obj_param_dict["--arg2 -b"].long_summary, "A true description of this parameter.") self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[0], {"string": "Number range: -5.0 to 5.0"}) self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[1]['link'], { "url": "https://www.foo.com", "title": "foo" }) self.assertEqual(obj_param_dict["--arg2 -b"].value_sources[2]['link'], { "command": "az test show", "title": "Show test details" })
def make_rst(self): INDENT = ' ' DOUBLEINDENT = INDENT * 2 az_cli = AzCli(cli_name='az', commands_loader_cls=MainCommandsLoader, invocation_cls=AzCliCommandInvoker, parser_cls=AzCliCommandParser, help_cls=AzCliHelp) with patch('getpass.getuser', return_value='your_system_user_login_name'): create_invoker_and_load_cmds_and_args(az_cli) help_files = get_all_help(az_cli) doc_source_map = _load_doc_source_map() for help_file in help_files: is_command = isinstance(help_file, CliCommandHelpFile) yield '.. cli{}:: {}'.format('command' if is_command else 'group', help_file.command if help_file.command else 'az') #it is top level group az if command is empty yield '' yield '{}:summary: {}'.format(INDENT, help_file.short_summary) yield '{}:description: {}'.format(INDENT, help_file.long_summary) if help_file.deprecate_info: yield '{}:deprecated: {}'.format(INDENT, help_file.deprecate_info._get_message(help_file.deprecate_info)) if not is_command: top_group_name = help_file.command.split()[0] if help_file.command else 'az' yield '{}:docsource: {}'.format(INDENT, doc_source_map[top_group_name] if top_group_name in doc_source_map else '') else: top_command_name = help_file.command.split()[0] if help_file.command else '' if top_command_name in doc_source_map: yield '{}:docsource: {}'.format(INDENT, doc_source_map[top_command_name]) yield '' if is_command and help_file.parameters: group_registry = ArgumentGroupRegistry( [p.group_name for p in help_file.parameters if p.group_name]) for arg in sorted(help_file.parameters, key=lambda p: group_registry.get_group_priority(p.group_name) + str(not p.required) + p.name): yield '{}.. cliarg:: {}'.format(INDENT, arg.name) yield '' yield '{}:required: {}'.format(DOUBLEINDENT, arg.required) if arg.deprecate_info: yield '{}:deprecated: {}'.format(DOUBLEINDENT, arg.deprecate_info._get_message(arg.deprecate_info)) short_summary = arg.short_summary or '' possible_values_index = short_summary.find(' Possible values include') short_summary = short_summary[0:possible_values_index if possible_values_index >= 0 else len(short_summary)] short_summary = short_summary.strip() yield '{}:summary: {}'.format(DOUBLEINDENT, short_summary) yield '{}:description: {}'.format(DOUBLEINDENT, arg.long_summary) if arg.choices: yield '{}:values: {}'.format(DOUBLEINDENT, ', '.join(sorted([str(x) for x in arg.choices]))) if arg.default and arg.default != argparse.SUPPRESS: try: if arg.default.startswith(USER_HOME): arg.default = arg.default.replace(USER_HOME, '~').replace('\\', '/') except Exception: pass try: arg.default = arg.default.replace("\\", "\\\\") except Exception: pass yield '{}:default: {}'.format(DOUBLEINDENT, arg.default) if arg.value_sources: yield '{}:source: {}'.format(DOUBLEINDENT, ', '.join(_get_populator_commands(arg))) yield '' yield '' if len(help_file.examples) > 0: for e in help_file.examples: yield '{}.. cliexample:: {}'.format(INDENT, e.short_summary) yield '' yield DOUBLEINDENT + e.command.replace("\\", "\\\\") yield ''
def run_linter(modules=None, rule_types=None, rules=None): require_azure_cli() from azure.cli.core import get_default_cli # pylint: disable=import-error from azure.cli.core.file_util import ( # pylint: disable=import-error get_all_help, create_invoker_and_load_cmds_and_args) heading('CLI Linter') # needed to remove helps from azdev azdev_helps = helps.copy() exclusions = {} selected_modules = get_path_table(include_only=modules) if not selected_modules: raise CLIError('No modules selected.') selected_mod_names = list(selected_modules['mod'].keys()) + list(selected_modules['core'].keys()) + \ list(selected_modules['ext'].keys()) selected_mod_paths = list(selected_modules['mod'].values()) + list(selected_modules['core'].values()) + \ list(selected_modules['ext'].values()) if selected_mod_names: display('Modules: {}\n'.format(', '.join(selected_mod_names))) # collect all rule exclusions for path in selected_mod_paths: exclusion_path = os.path.join(path, 'linter_exclusions.yml') if os.path.isfile(exclusion_path): mod_exclusions = yaml.load(open(exclusion_path)) exclusions.update(mod_exclusions) start = time.time() display('Initializing linter with command table and help files...') az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) stop = time.time() logger.info('Commands and help loaded in %i sec', stop - start) command_loader = az_cli.invocation.commands_loader # format loaded help loaded_help = {data.command: data for data in loaded_help if data.command} # load yaml help help_file_entries = {} for entry_name, help_yaml in helps.items(): # ignore help entries from azdev itself, unless it also coincides # with a CLI or extension command name. if entry_name in azdev_helps and entry_name not in command_loader.command_table: continue help_entry = yaml.load(help_yaml) help_file_entries[entry_name] = help_entry # trim command table and help to just selected_modules command_loader, help_file_entries = filter_modules( command_loader, help_file_entries, modules=selected_mod_names) if not command_loader.command_table: raise CLIError('No commands selected to check.') # Instantiate and run Linter linter_manager = LinterManager(command_loader=command_loader, help_file_entries=help_file_entries, loaded_help=loaded_help, exclusions=exclusions, rule_inclusions=rules) subheading('Results') logger.info('Running linter: %i commands, %i help entries', len(command_loader.command_table), len(help_file_entries)) exit_code = linter_manager.run( run_params=not rule_types or 'params' in rule_types, run_commands=not rule_types or 'commands' in rule_types, run_command_groups=not rule_types or 'command_groups'in rule_types, run_help_files_entries=not rule_types or 'help_entries' in rule_types) sys.exit(exit_code)
def _get_help_files(self, az_cli): create_invoker_and_load_cmds_and_args(az_cli) return get_all_help(az_cli)
def main(args): from azure.cli.core import get_default_cli from azure.cli.core.file_util import get_all_help, create_invoker_and_load_cmds_and_args print('Initializing linter with command table and help files...') # setup CLI to enable command loader az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) command_table = az_cli.invocation.commands_loader.command_table # format loaded help loaded_help = {data.command: data for data in loaded_help if data.command} # load yaml help help_file_entries = {} for entry_name, help_yaml in helps.items(): help_entry = yaml.load(help_yaml) help_file_entries[entry_name] = help_entry if not args.rule_types_to_run: args.rule_types_to_run = ['params', 'commands', 'command_groups', 'help_entries'] # find rule exclusions and pass to linter manager from ..utilities.path import get_command_modules_paths exclusions = {} command_modules_paths = get_command_modules_paths() for _, path in get_command_modules_paths(): exclusion_path = os.path.join(path, 'linter_exclusions.yml') if os.path.isfile(exclusion_path): mod_exclusions = yaml.load(open(exclusion_path)) exclusions.update(mod_exclusions) # only run linter on modules specified if args.modules: allowed_module_paths = tuple(path for mod, path in command_modules_paths if mod in args.modules) for command_name, command in list(command_table.items()): # brute force way to remove all traces from excluded modules loader_cls = command.loader.__class__ loader_file_path = inspect.getfile(loader_cls) if not loader_file_path.startswith(allowed_module_paths): del command_table[command_name] help_file_entries.pop(command_name, None) for group_name in _get_command_groups(command_name): help_file_entries.pop(group_name, None) # Instantiate and run Linter linter_manager = LinterManager(command_table=command_table, help_file_entries=help_file_entries, loaded_help=loaded_help, exclusions=exclusions) exit_code = linter_manager.run(run_params='params' in args.rule_types_to_run, run_commands='commands' in args.rule_types_to_run, run_command_groups='command_groups' in args.rule_types_to_run, run_help_files_entries='help_entries' in args.rule_types_to_run, ci=args.ci) sys.exit(exit_code)
def run_linter(modules=None, rule_types=None, rules=None, ci_exclusions=None, git_source=None, git_target=None, git_repo=None, include_whl_extensions=False, min_severity=None, save_global_exclusion=False): require_azure_cli() from azure.cli.core import get_default_cli # pylint: disable=import-error from azure.cli.core.file_util import ( # pylint: disable=import-error get_all_help, create_invoker_and_load_cmds_and_args) heading('CLI Linter') # allow user to run only on CLI or extensions cli_only = modules == ['CLI'] ext_only = modules == ['EXT'] if cli_only or ext_only: modules = None # process severity option if min_severity: try: min_severity = LinterSeverity.get_linter_severity(min_severity) except ValueError: valid_choices = linter_severity_choices() raise CLIError( "Please specify a valid linter severity. It should be one of: {}" .format(", ".join(valid_choices))) # needed to remove helps from azdev azdev_helps = helps.copy() exclusions = {} selected_modules = get_path_table( include_only=modules, include_whl_extensions=include_whl_extensions) if cli_only: selected_modules['ext'] = {} if ext_only: selected_modules['mod'] = {} selected_modules['core'] = {} # used to upsert global exclusion update_global_exclusion = None if save_global_exclusion and (cli_only or ext_only): if cli_only: update_global_exclusion = 'CLI' if os.path.exists( os.path.join(get_cli_repo_path(), 'linter_exclusions.yml')): os.remove( os.path.join(get_cli_repo_path(), 'linter_exclusions.yml')) elif ext_only: update_global_exclusion = 'EXT' for ext_path in get_ext_repo_paths(): if os.path.exists( os.path.join(ext_path, 'linter_exclusions.yml')): os.remove(os.path.join(ext_path, 'linter_exclusions.yml')) # filter down to only modules that have changed based on git diff selected_modules = filter_by_git_diff(selected_modules, git_source, git_target, git_repo) if not any((selected_modules[x] for x in selected_modules)): raise CLIError('No modules selected.') selected_mod_names = list(selected_modules['mod'].keys()) + list(selected_modules['core'].keys()) + \ list(selected_modules['ext'].keys()) selected_mod_paths = list(selected_modules['mod'].values()) + list(selected_modules['core'].values()) + \ list(selected_modules['ext'].values()) if selected_mod_names: display('Modules: {}\n'.format(', '.join(selected_mod_names))) # collect all rule exclusions for path in selected_mod_paths: exclusion_path = os.path.join(path, 'linter_exclusions.yml') if os.path.isfile(exclusion_path): mod_exclusions = yaml.safe_load(open(exclusion_path)) merge_exclusion(exclusions, mod_exclusions or {}) global_exclusion_paths = [ os.path.join(get_cli_repo_path(), 'linter_exclusions.yml') ] try: global_exclusion_paths.extend([ os.path.join(path, 'linter_exclusions.yml') for path in (get_ext_repo_paths() or []) ]) except CLIError: pass for path in global_exclusion_paths: if os.path.isfile(path): mod_exclusions = yaml.safe_load(open(path)) merge_exclusion(exclusions, mod_exclusions or {}) start = time.time() display('Initializing linter with command table and help files...') az_cli = get_default_cli() # load commands, args, and help create_invoker_and_load_cmds_and_args(az_cli) loaded_help = get_all_help(az_cli) stop = time.time() logger.info('Commands and help loaded in %i sec', stop - start) command_loader = az_cli.invocation.commands_loader # format loaded help loaded_help = {data.command: data for data in loaded_help if data.command} # load yaml help help_file_entries = {} for entry_name, help_yaml in helps.items(): # ignore help entries from azdev itself, unless it also coincides # with a CLI or extension command name. if entry_name in azdev_helps and entry_name not in command_loader.command_table: continue help_entry = yaml.safe_load(help_yaml) help_file_entries[entry_name] = help_entry # trim command table and help to just selected_modules command_loader, help_file_entries = filter_modules( command_loader, help_file_entries, modules=selected_mod_names, include_whl_extensions=include_whl_extensions) if not command_loader.command_table: raise CLIError('No commands selected to check.') # Instantiate and run Linter linter_manager = LinterManager( command_loader=command_loader, help_file_entries=help_file_entries, loaded_help=loaded_help, exclusions=exclusions, rule_inclusions=rules, use_ci_exclusions=ci_exclusions, min_severity=min_severity, update_global_exclusion=update_global_exclusion) subheading('Results') logger.info('Running linter: %i commands, %i help entries', len(command_loader.command_table), len(help_file_entries)) exit_code = linter_manager.run(run_params=not rule_types or 'params' in rule_types, run_commands=not rule_types or 'commands' in rule_types, run_command_groups=not rule_types or 'command_groups' in rule_types, run_help_files_entries=not rule_types or 'help_entries' in rule_types) sys.exit(exit_code)