Skip to content

Commit 7be0208

Browse files
feature: add rebuild-index command
1 parent c59bfe4 commit 7be0208

File tree

6 files changed

+50
-40
lines changed

6 files changed

+50
-40
lines changed

src/azure-cli-core/azure/cli/core/__init__.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ def _get_extension_suppressions(mod_loaders):
517517
else:
518518
logger.debug("No module found from index for '%s'", args)
519519
if command_index_is_valid and command_index_has_entries and args and not args[0].startswith('-') and \
520-
not self.cli_ctx.data['completer_active']:
520+
not self.cli_ctx.data['completer_active'] and args[0] != 'rebuild-index':
521521
logger.debug("Valid command index has no entry for '%s'. Skip full reload and fail fast.",
522522
args[0])
523523
return self.command_table
@@ -895,6 +895,28 @@ def invalidate(self):
895895
self.INDEX[self._HELP_INDEX] = {}
896896
logger.debug("Command index has been invalidated.")
897897

898+
def rebuild(self):
899+
"""Invalidate command index and rebuild it if command index is enabled."""
900+
self.invalidate()
901+
902+
if not self.cli_ctx:
903+
return
904+
905+
if not self.cli_ctx.config.getboolean('core', 'use_command_index', fallback=True):
906+
return
907+
908+
invocation = getattr(self.cli_ctx, 'invocation', None)
909+
commands_loader = getattr(invocation, 'commands_loader', None) if invocation else None
910+
if not commands_loader:
911+
logger.debug('Skipping command index rebuild because commands_loader is unavailable.')
912+
return
913+
914+
try:
915+
logger.debug('Rebuilding command index after extension operation.')
916+
commands_loader.load_command_table(None)
917+
except Exception as ex: # pylint: disable=broad-except
918+
logger.warning('Failed to rebuild command index after extension operation: %s', ex)
919+
898920

899921
class ModExtensionSuppress: # pylint: disable=too-few-public-methods
900922

src/azure-cli-core/azure/cli/core/extension/operations.py

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -276,29 +276,6 @@ def _augment_telemetry_with_ext_info(extension_name, ext=None):
276276
pass
277277

278278

279-
def _rebuild_command_index(cli_ctx):
280-
command_index = CommandIndex(cli_ctx)
281-
command_index.invalidate()
282-
283-
if not cli_ctx:
284-
return
285-
286-
if not cli_ctx.config.getboolean('core', 'use_command_index', fallback=True):
287-
return
288-
289-
invocation = getattr(cli_ctx, 'invocation', None)
290-
commands_loader = getattr(invocation, 'commands_loader', None) if invocation else None
291-
if not commands_loader:
292-
logger.debug('Skipping command index rebuild because commands_loader is unavailable.')
293-
return
294-
295-
try:
296-
logger.debug('Rebuilding command index after extension operation.')
297-
commands_loader.load_command_table(None)
298-
except Exception as ex: # pylint: disable=broad-except
299-
logger.warning('Failed to rebuild command index after extension operation: %s', ex)
300-
301-
302279
def check_version_compatibility(azext_metadata):
303280
is_compatible, cli_core_version, min_required, max_required, min_ext_required = ext_compat_with_cli(azext_metadata)
304281
# logger.debug("Extension compatibility result: is_compatible=%s cli_core_version=%s min_required=%s "
@@ -375,7 +352,7 @@ def add_extension(cmd=None, source=None, extension_name=None, index_url=None, ye
375352
except ExtensionNotInstalledException:
376353
pass
377354

378-
_rebuild_command_index(cmd_cli_ctx)
355+
CommandIndex(cmd_cli_ctx).rebuild()
379356

380357

381358
def is_cloud_shell_system_extension(ext_path):
@@ -399,7 +376,7 @@ def remove_extension(extension_name, cli_ctx=None):
399376
# We call this just before we remove the extension so we can get the metadata before it is gone
400377
_augment_telemetry_with_ext_info(extension_name, ext)
401378
rmtree_with_retry(ext.path)
402-
_rebuild_command_index(cli_ctx)
379+
CommandIndex(cli_ctx).rebuild()
403380
except ExtensionNotInstalledException as e:
404381
raise CLIError(e)
405382

@@ -459,7 +436,7 @@ def update_extension(cmd=None, extension_name=None, index_url=None, pip_extra_in
459436
logger.debug('Copying %s to %s', backup_dir, extension_path)
460437
shutil.copytree(backup_dir, extension_path)
461438
raise CLIError('Failed to update. Rolled {} back to {}.'.format(extension_name, cur_version))
462-
_rebuild_command_index(cmd_cli_ctx)
439+
CommandIndex(cmd_cli_ctx).rebuild()
463440
except ExtensionNotInstalledException as e:
464441
raise CLIError(e)
465442

src/azure-cli-core/azure/cli/core/tests/test_extension.py

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010

1111
from unittest import mock
1212
from azure.cli.core.mock import DummyCli
13+
from azure.cli.core import CommandIndex
1314

1415
from azure.cli.core.extension import (get_extensions, build_extension_path, extension_exists,
1516
get_extension, get_extension_names, get_extension_modname, ext_compat_with_cli,
1617
ExtensionNotInstalledException, WheelExtension,
1718
EXTENSIONS_MOD_PREFIX, EXT_METADATA_MINCLICOREVERSION, EXT_METADATA_MAXCLICOREVERSION)
1819

1920
from azure.cli.core.extension.operations import list_available_extensions, add_extension, show_extension, remove_extension, list_extensions, update_extension
20-
from azure.cli.core.extension import operations as extension_operations
2121

2222
# The test extension name
2323
EXT_NAME = 'myfirstcliextension'
@@ -108,35 +108,32 @@ def test_rebuild_command_index_when_enabled(self):
108108
cli_ctx.config.getboolean.return_value = True
109109
cli_ctx.invocation.commands_loader = mock.MagicMock()
110110

111-
with mock.patch('azure.cli.core.extension.operations.CommandIndex') as command_index:
112-
extension_operations._rebuild_command_index(cli_ctx) # pylint: disable=protected-access
111+
with mock.patch.object(CommandIndex, 'invalidate') as invalidate:
112+
CommandIndex(cli_ctx).rebuild()
113113

114-
command_index.assert_called_once_with(cli_ctx)
115-
command_index.return_value.invalidate.assert_called_once()
114+
invalidate.assert_called_once()
116115
cli_ctx.invocation.commands_loader.load_command_table.assert_called_once_with(None)
117116

118117
def test_rebuild_command_index_skips_when_disabled(self):
119118
cli_ctx = mock.MagicMock()
120119
cli_ctx.config.getboolean.return_value = False
121120
cli_ctx.invocation.commands_loader = mock.MagicMock()
122121

123-
with mock.patch('azure.cli.core.extension.operations.CommandIndex') as command_index:
124-
extension_operations._rebuild_command_index(cli_ctx) # pylint: disable=protected-access
122+
with mock.patch.object(CommandIndex, 'invalidate') as invalidate:
123+
CommandIndex(cli_ctx).rebuild()
125124

126-
command_index.assert_called_once_with(cli_ctx)
127-
command_index.return_value.invalidate.assert_called_once()
125+
invalidate.assert_called_once()
128126
cli_ctx.invocation.commands_loader.load_command_table.assert_not_called()
129127

130128
def test_rebuild_command_index_skips_without_loader(self):
131129
cli_ctx = mock.MagicMock()
132130
cli_ctx.config.getboolean.return_value = True
133131
cli_ctx.invocation = None
134132

135-
with mock.patch('azure.cli.core.extension.operations.CommandIndex') as command_index:
136-
extension_operations._rebuild_command_index(cli_ctx) # pylint: disable=protected-access
133+
with mock.patch.object(CommandIndex, 'invalidate') as invalidate:
134+
CommandIndex(cli_ctx).rebuild()
137135

138-
command_index.assert_called_once_with(cli_ctx)
139-
command_index.return_value.invalidate.assert_called_once()
136+
invalidate.assert_called_once()
140137

141138
def test_no_extensions_in_dir(self):
142139
""" Directory exists but there are no extensions """

src/azure-cli/azure/cli/command_modules/util/_help.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@
5757
short-summary: Upgrade Azure CLI and extensions
5858
"""
5959

60+
helps['rebuild-index'] = """
61+
type: command
62+
short-summary: Rebuild the Azure CLI command index cache.
63+
"""
64+
6065
helps['demo'] = """
6166
type: group
6267
short-summary: Demos for designing, developing and demonstrating Azure CLI.

src/azure-cli/azure/cli/command_modules/util/commands.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ def load_command_table(self, _):
1515
with self.command_group('') as g:
1616
g.custom_command('upgrade', 'upgrade_version', is_preview=True)
1717

18+
with self.command_group('') as g:
19+
g.custom_command('rebuild-index', 'rebuild_index')
20+
1821
with self.command_group('demo', deprecate_info=g.deprecate(hide=True)) as g:
1922
g.custom_command('style', 'demo_style')
2023
g.custom_command('byo-access-token', 'byo_access_token')

src/azure-cli/azure/cli/command_modules/util/custom.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def show_version(cmd): # pylint: disable=unused-argument
3939
return versions
4040

4141

42+
def rebuild_index(cmd):
43+
from azure.cli.core import CommandIndex
44+
CommandIndex(cmd.cli_ctx).rebuild()
45+
return {'status': 'rebuilt'}
46+
47+
4248
def upgrade_version(cmd, update_all=None, yes=None, allow_preview=None): # pylint: disable=too-many-locals, too-many-statements, too-many-branches, no-member, unused-argument
4349
import platform
4450
import sys

0 commit comments

Comments
 (0)