def _run_dispatcher(dispatcher: craft_cli.Dispatcher) -> None: global_args = dispatcher.pre_parse_args(sys.argv[1:]) if global_args.get("version"): emit.message(f"snapcraft {__version__}") else: if global_args.get("trace"): emit.message( "Options -t and --trace are deprecated, use --verbosity=debug instead." ) emit.set_mode(EmitterMode.DEBUG) dispatcher.load_command(None) dispatcher.run() emit.ended_ok()
def run(): """Run the CLI.""" dispatcher = get_dispatcher() try: _run_dispatcher(dispatcher) retcode = 0 except ArgumentParsingError as err: # TODO https://github.com/canonical/craft-cli/issues/78 with contextlib.suppress(KeyError, IndexError): if ( err.__context__ is not None and err.__context__.args[0] # pylint: disable=no-member not in dispatcher.commands ): run_legacy(err) print(err, file=sys.stderr) # to stderr, as argparse normally does emit.ended_ok() retcode = 1 except ProvideHelpException as err: print(err, file=sys.stderr) # to stderr, as argparse normally does emit.ended_ok() retcode = 0 except errors.LegacyFallback as err: run_legacy(err) except craft_store.errors.NoKeyringError as err: emit.error( craft_cli.errors.CraftError( f"craft-store error: {err}", resolution=( "Ensure the keyring is working or " f"{store.constants.ENVIRONMENT_STORE_CREDENTIALS} " "is correctly exported into the environment" ), docs_url="https://snapcraft.io/docs/snapcraft-authentication", ) ) retcode = 1 except craft_store.errors.CraftStoreError as err: emit.error(craft_cli.errors.CraftError(f"craft-store error: {err}")) retcode = 1 except errors.LinterError as err: emit.error(craft_cli.errors.CraftError(f"linter error: {err}")) retcode = err.exit_code except errors.SnapcraftError as err: emit.error(err) retcode = 1 return retcode
def run_legacy(err: Optional[Exception] = None): """Run legacy implementation.""" # Reset the libraries to their original log level for lib_name in _LIB_NAMES: logger = logging.getLogger(lib_name) logger.setLevel(_ORIGINAL_LIB_NAME_LOG_LEVEL[lib_name]) snapcraft.BasePlugin = snapcraft_legacy.BasePlugin # type: ignore snapcraft.ProjectOptions = snapcraft_legacy.ProjectOptions # type: ignore # Legacy does not use craft-cli if err is not None: emit.trace(f"run legacy implementation: {err!s}") emit.ended_ok() legacy.legacy_run()
def main(argv=None): """Provide the main entry point.""" if env.is_charmcraft_running_in_managed_mode(): logpath = env.get_managed_environment_log_path() else: logpath = None emit.init( EmitterMode.NORMAL, "charmcraft", f"Starting charmcraft version {__version__}", log_filepath=logpath, ) if argv is None: argv = sys.argv extra_global_options = [ GlobalArgument( "project_dir", "option", "-p", "--project-dir", "Specify the project's directory (defaults to current)", ), ] # process try: setup_parts() # load the dispatcher and put everything in motion dispatcher = Dispatcher( "charmcraft", COMMAND_GROUPS, summary=GENERAL_SUMMARY, extra_global_args=extra_global_options, ) global_args = dispatcher.pre_parse_args(argv[1:]) loaded_config = config.load(global_args["project_dir"]) command = dispatcher.load_command(loaded_config) if command.needs_config and not loaded_config.project.config_provided: raise ArgumentParsingError( "The specified command needs a valid 'charmcraft.yaml' configuration file (in " "the current directory or where specified with --project-dir option); see " "the reference: https://discourse.charmhub.io/t/charmcraft-configuration/4138" ) emit.trace(_get_system_details()) retcode = dispatcher.run() except ArgumentParsingError as err: print(err, file=sys.stderr) # to stderr, as argparse normally does emit.ended_ok() retcode = 1 except ProvideHelpException as err: print(err, file=sys.stderr) # to stderr, as argparse normally does emit.ended_ok() retcode = 0 except CraftError as err: emit.error(err) retcode = err.retcode except errors.CraftStoreError as err: error = CraftError(f"craft-store error: {err}") emit.error(error) retcode = 1 except KeyboardInterrupt as exc: error = CraftError("Interrupted.") error.__cause__ = exc emit.error(error) retcode = 1 except Exception as err: error = CraftError(f"charmcraft internal error: {err!r}") error.__cause__ = err emit.error(error) retcode = 1 else: emit.ended_ok() if retcode is None: retcode = 0 return retcode