Skip to content

Commit d3c663e

Browse files
authored
Azdev enhancement for working with CI. (#115)
* Azdev enhancement for working with CI. * Enhance diffing mechanism. * Remove unused test files. * Fix issues when installed from edge build.
1 parent 15a26c1 commit d3c663e

23 files changed

+427
-326
lines changed

HISTORY.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,21 @@
33
Release History
44
===============
55

6+
0.1.5
7+
++++++
8+
* `azdev extension add/remove`: Added ability to supply wildcard (*) to install all available dev extensions
9+
and remove all installed dev extensions.
10+
* `azdev setup`: Added ability to install all extensions using `--ext/-e *`. Added ability to install CLI edge build
11+
with `--cli/-c EDGE`.
12+
* `azdev style/test/linter`: Add special names CLI and EXT to allow running on just CLI modules or just extensions.
13+
Add new argument group `--tgt`, `--src`, `--repo` to allow checking only modules or
14+
extensions which have changed based on a git diff.
15+
* `azdev linter`: Added `--include-whl-extensions` flag to permit running the linter on extensions installed using
16+
the `az extension add` command.
17+
* `azdev verify license`: Command will not check any dev-installed CLI and extension repos. Previously, it only
18+
checked the CLI repo.
19+
* Added new commands `azdev cli/extension generate-docs` to generate sphinx documentation.
20+
621
0.1.4
722
++++++
823
* `azdev linter`: Fix issue with help example rule.

azdev/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
# license information.
55
# -----------------------------------------------------------------------------
66

7-
__VERSION__ = '0.1.4'
7+
__VERSION__ = '0.1.5'

azdev/commands.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,13 +62,3 @@ def operation_group(name):
6262

6363
with CommandGroup(self, 'extension', operation_group('help')) as g:
6464
g.command('generate-docs', 'generate_extension_ref_docs')
65-
66-
# TODO: implement
67-
# with CommandGroup(self, 'coverage', command_path) as g:
68-
# g.command('command', 'command_coverage')
69-
# g.command('code', 'code_coverage')
70-
71-
# TODO: implement
72-
# with CommandGroup(self, 'verify', command_path) as g:
73-
# g.command('package', 'verify_packages')
74-
# g.command('dependencies', 'verify_dependencies')

azdev/help.py

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,6 @@
6565
"""
6666

6767

68-
helps['coverage'] = """
69-
short-summary: Test coverage statistics and reports.
70-
"""
71-
72-
73-
helps['coverage code'] = """
74-
short-summary: Run CLI tests with code coverage.
75-
"""
76-
77-
78-
helps['coverage command'] = """
79-
short-summary: Analyze CLI test run data for command and argument coverage.
80-
long-summary: This does not run any tests!
81-
"""
82-
83-
8468
helps['verify'] = """
8569
short-summary: Verify CLI product features.
8670
"""
@@ -113,6 +97,9 @@
11397

11498
helps['style'] = """
11599
short-summary: Check code style (pylint and PEP8).
100+
examples:
101+
- name: Check style for only those modules which have changed based on a git diff.
102+
text: azdev style --repo azure-cli --tgt upstream/master --src upstream/dev
116103
"""
117104

118105

@@ -134,11 +121,17 @@
134121
135122
- name: Run tests for a module but run the tests that failed last time first.
136123
text: azdev test {mod} -a --ff
124+
125+
- name: Run tests for only those modules which have changed based on a git diff.
126+
text: azdev test --repo azure-cli --tgt upstream/master --src upstream/dev
137127
"""
138128

139129

140130
helps['linter'] = """
141131
short-summary: Static code checks of the CLI command table.
132+
examples:
133+
- name: Check linter rules for only those modules which have changed based on a git diff.
134+
text: azdev linter --repo azure-cli --tgt upstream/master --src upstream/dev
142135
"""
143136

144137

@@ -152,16 +145,6 @@
152145
"""
153146

154147

155-
helps['sdk'] = """
156-
short-summary: Perform quick Python SDK operations.
157-
"""
158-
159-
160-
helps['sdk draft'] = """
161-
short-summary: Install draft packages from the Python SDK repo.
162-
"""
163-
164-
165148
helps['extension'] = """
166149
short-summary: Control which CLI extensions are visible in the development environment.
167150
"""

azdev/operations/extensions/__init__.py

Lines changed: 32 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,19 @@ def add_extension(extensions):
2626
ext_paths = get_ext_repo_paths()
2727
all_extensions = find_files(ext_paths, 'setup.py')
2828

29-
paths_to_add = []
30-
for path in all_extensions:
31-
folder = os.path.dirname(path)
32-
long_name = os.path.basename(folder)
33-
if long_name in extensions:
34-
paths_to_add.append(folder)
35-
extensions.remove(long_name)
36-
# raise error if any extension wasn't found
37-
if extensions:
38-
raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
29+
if extensions == ['*']:
30+
paths_to_add = [os.path.dirname(path) for path in all_extensions if 'site-packages' not in path]
31+
else:
32+
paths_to_add = []
33+
for path in all_extensions:
34+
folder = os.path.dirname(path)
35+
long_name = os.path.basename(folder)
36+
if long_name in extensions:
37+
paths_to_add.append(folder)
38+
extensions.remove(long_name)
39+
# raise error if any extension wasn't found
40+
if extensions:
41+
raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
3942

4043
for path in paths_to_add:
4144
result = pip_cmd('install -e {}'.format(path), "Adding extension '{}'...".format(path))
@@ -49,16 +52,20 @@ def remove_extension(extensions):
4952
installed_paths = find_files(ext_paths, '*.*-info')
5053
paths_to_remove = []
5154
names_to_remove = []
52-
for path in installed_paths:
53-
folder = os.path.dirname(path)
54-
long_name = os.path.basename(folder)
55-
if long_name in extensions:
56-
paths_to_remove.append(folder)
57-
names_to_remove.append(long_name)
58-
extensions.remove(long_name)
59-
# raise error if any extension not installed
60-
if extensions:
61-
raise CLIError('extension(s) not installed: {}'.format(' '.join(extensions)))
55+
if extensions == ['*']:
56+
paths_to_remove = [os.path.dirname(path) for path in installed_paths]
57+
names_to_remove = [os.path.basename(os.path.dirname(path)) for path in installed_paths]
58+
else:
59+
for path in installed_paths:
60+
folder = os.path.dirname(path)
61+
long_name = os.path.basename(folder)
62+
if long_name in extensions:
63+
paths_to_remove.append(folder)
64+
names_to_remove.append(long_name)
65+
extensions.remove(long_name)
66+
# raise error if any extension not installed
67+
if extensions:
68+
raise CLIError('extension(s) not installed: {}'.format(' '.join(extensions)))
6269

6370
# removes any links that may have been added to site-packages.
6471
for ext in names_to_remove:
@@ -113,12 +120,17 @@ def list_extensions():
113120
except IndexError:
114121
continue
115122

123+
# ignore anything in site-packages folder
124+
if 'site-packages' in ext_path:
125+
continue
126+
116127
folder = os.path.dirname(ext_path)
117128
long_name = os.path.basename(folder)
118129
if long_name not in installed_names:
119130
results.append({'name': long_name, 'install': '', 'path': folder})
120131
else:
121132
results.append({'name': long_name, 'install': 'Installed', 'path': folder})
133+
122134
return results
123135

124136

azdev/operations/legal.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from knack.util import CLIError
1010

1111
from azdev.utilities import (
12-
display, heading, subheading, get_cli_repo_path)
12+
display, heading, subheading, get_cli_repo_path, get_ext_repo_paths)
1313

1414

1515
LICENSE_HEADER = """# --------------------------------------------------------------------------------------------
@@ -18,25 +18,32 @@
1818
# --------------------------------------------------------------------------------------------
1919
"""
2020

21+
_IGNORE_SUBDIRS = ['__pycache__', 'vendored_sdks', 'site-packages', 'env']
22+
2123

2224
def check_license_headers():
2325

2426
heading('Verify License Headers')
2527

2628
cli_path = get_cli_repo_path()
27-
env_path = os.path.join(cli_path, 'env')
29+
all_paths = [cli_path]
30+
for path in get_ext_repo_paths():
31+
all_paths.append(path)
2832

2933
files_without_header = []
30-
for current_dir, _, files in os.walk(cli_path):
31-
if current_dir.startswith(env_path):
32-
continue
33-
34-
file_itr = (os.path.join(current_dir, p) for p in files if p.endswith('.py') and p != 'azure_bdist_wheel.py')
35-
for python_file in file_itr:
36-
with open(python_file, 'r', encoding='utf-8') as f:
37-
file_text = f.read()
38-
if file_text and LICENSE_HEADER not in file_text:
39-
files_without_header.append(os.path.join(current_dir, python_file))
34+
for path in all_paths:
35+
for current_dir, subdirs, files in os.walk(path):
36+
for i, x in enumerate(subdirs):
37+
if x in _IGNORE_SUBDIRS or x.startswith('.'):
38+
del subdirs[i]
39+
40+
# pylint: disable=line-too-long
41+
file_itr = (os.path.join(current_dir, p) for p in files if p.endswith('.py') and p != 'azure_bdist_wheel.py')
42+
for python_file in file_itr:
43+
with open(python_file, 'r', encoding='utf-8') as f:
44+
file_text = f.read()
45+
if file_text and LICENSE_HEADER not in file_text:
46+
files_without_header.append(os.path.join(current_dir, python_file))
4047

4148
subheading('Results')
4249
if files_without_header:

azdev/operations/linter/__init__.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from knack.util import CLIError
1717

1818
from azdev.utilities import (
19-
heading, subheading, display, get_path_table, require_azure_cli)
19+
heading, subheading, display, get_path_table, require_azure_cli, filter_by_git_diff)
2020

2121
from .linter import LinterManager, LinterScope, RuleError
2222
from .util import filter_modules
@@ -26,7 +26,8 @@
2626

2727

2828
# pylint:disable=too-many-locals
29-
def run_linter(modules=None, rule_types=None, rules=None, ci_exclusions=None):
29+
def run_linter(modules=None, rule_types=None, rules=None, ci_exclusions=None,
30+
git_source=None, git_target=None, git_repo=None, include_whl_extensions=False):
3031

3132
require_azure_cli()
3233

@@ -36,12 +37,27 @@ def run_linter(modules=None, rule_types=None, rules=None, ci_exclusions=None):
3637

3738
heading('CLI Linter')
3839

40+
# allow user to run only on CLI or extensions
41+
cli_only = modules == ['CLI']
42+
ext_only = modules == ['EXT']
43+
if cli_only or ext_only:
44+
modules = None
45+
3946
# needed to remove helps from azdev
4047
azdev_helps = helps.copy()
4148
exclusions = {}
42-
selected_modules = get_path_table(include_only=modules)
49+
selected_modules = get_path_table(include_only=modules, include_whl_extensions=include_whl_extensions)
50+
51+
if cli_only:
52+
selected_modules['ext'] = {}
53+
if ext_only:
54+
selected_modules['mod'] = {}
55+
selected_modules['core'] = {}
56+
57+
# filter down to only modules that have changed based on git diff
58+
selected_modules = filter_by_git_diff(selected_modules, git_source, git_target, git_repo)
4359

44-
if not selected_modules:
60+
if not any((selected_modules[x] for x in selected_modules)):
4561
raise CLIError('No modules selected.')
4662

4763
selected_mod_names = list(selected_modules['mod'].keys()) + list(selected_modules['core'].keys()) + \
@@ -86,7 +102,7 @@ def run_linter(modules=None, rule_types=None, rules=None, ci_exclusions=None):
86102

87103
# trim command table and help to just selected_modules
88104
command_loader, help_file_entries = filter_modules(
89-
command_loader, help_file_entries, modules=selected_mod_names)
105+
command_loader, help_file_entries, modules=selected_mod_names, include_whl_extensions=include_whl_extensions)
90106

91107
if not command_loader.command_table:
92108
raise CLIError('No commands selected to check.')

azdev/operations/linter/util.py

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from knack.log import get_logger
1111

12-
from azdev.utilities import COMMAND_MODULE_PREFIX
12+
from azdev.utilities import get_name_index
1313

1414

1515
logger = get_logger(__name__)
@@ -18,29 +18,30 @@
1818
_LOADER_CLS_RE = re.compile('.*azure/cli/command_modules/(?P<module>[^/]*)/__init__.*')
1919

2020

21-
def filter_modules(command_loader, help_file_entries, modules=None):
21+
def filter_modules(command_loader, help_file_entries, modules=None, include_whl_extensions=False):
2222
""" Modify the command table and help entries to only include certain modules/extensions.
2323
2424
: param command_loader: The CLICommandsLoader containing the command table to filter.
2525
: help_file_entries: The dict of HelpFile entries to filter.
2626
: modules: [str] list of module or extension names to retain.
2727
"""
28-
return _filter_mods(command_loader, help_file_entries, modules=modules)
28+
return _filter_mods(command_loader, help_file_entries, modules=modules,
29+
include_whl_extensions=include_whl_extensions)
2930

3031

31-
def exclude_commands(command_loader, help_file_entries, module_exclusions):
32+
def exclude_commands(command_loader, help_file_entries, module_exclusions, include_whl_extensions=False):
3233
""" Modify the command table and help entries to exclude certain modules/extensions.
3334
3435
: param command_loader: The CLICommandsLoader containing the command table to filter.
3536
: help_file_entries: The dict of HelpFile entries to filter.
3637
: modules: [str] list of module or extension names to remove.
3738
"""
38-
return _filter_mods(command_loader, help_file_entries, modules=module_exclusions, exclude=True)
39+
return _filter_mods(command_loader, help_file_entries, modules=module_exclusions, exclude=True,
40+
include_whl_extensions=include_whl_extensions)
3941

4042

41-
def _filter_mods(command_loader, help_file_entries, modules=None, exclude=False):
43+
def _filter_mods(command_loader, help_file_entries, modules=None, exclude=False, include_whl_extensions=False):
4244
modules = modules or []
43-
modules = [x.replace(COMMAND_MODULE_PREFIX, '') for x in modules]
4445

4546
# command tables and help entries must be copied to allow for seperate linter scope
4647
command_table = command_loader.command_table.copy()
@@ -49,6 +50,7 @@ def _filter_mods(command_loader, help_file_entries, modules=None, exclude=False)
4950
command_loader.command_table = command_table
5051
command_loader.command_group_table = command_group_table
5152
help_file_entries = help_file_entries.copy()
53+
name_index = get_name_index(include_whl_extensions=include_whl_extensions)
5254

5355
for command_name in list(command_loader.command_table.keys()):
5456
try:
@@ -58,7 +60,11 @@ def _filter_mods(command_loader, help_file_entries, modules=None, exclude=False)
5860
logger.warning(ex)
5961
source_name = None
6062

61-
is_specified = source_name in modules
63+
try:
64+
long_name = name_index[source_name]
65+
is_specified = source_name in modules or long_name in modules
66+
except KeyError:
67+
is_specified = False
6268
if is_specified == exclude:
6369
# brute force method of ignoring commands from a module or extension
6470
command_loader.command_table.pop(command_name, None)

0 commit comments

Comments
 (0)