azdev 0.2.10__tar.gz → 0.2.12__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.
- {azdev-0.2.10 → azdev-0.2.12}/HISTORY.rst +20 -0
- {azdev-0.2.10/azdev.egg-info → azdev-0.2.12}/PKG-INFO +23 -3
- {azdev-0.2.10 → azdev-0.2.12}/azdev/__init__.py +1 -1
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/code_gen.py +7 -2
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/extensions/__init__.py +37 -3
- azdev-0.2.12/azdev/operations/extensions/metadata.py +203 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/extensions/util.py +5 -5
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/setup.py +19 -13
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/testtool/pytest_runner.py +5 -4
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/__init__.py +2 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/command.py +20 -0
- {azdev-0.2.10 → azdev-0.2.12/azdev.egg-info}/PKG-INFO +23 -3
- {azdev-0.2.10 → azdev-0.2.12}/azdev.egg-info/SOURCES.txt +1 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev.egg-info/requires.txt +2 -2
- {azdev-0.2.10 → azdev-0.2.12}/setup.py +5 -2
- {azdev-0.2.10 → azdev-0.2.12}/LICENSE +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/MANIFEST.in +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/README.md +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/README.rst +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/__main__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/commands.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/completer.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/config/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/config/cli.flake8 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/config/cli_pylintrc +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/config/ext.flake8 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/config/ext_pylintrc +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/help.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/HISTORY.rst +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/README.rst +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/_client_factory.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/_help.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/_params.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/_validators.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/azext_metadata.json +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/blank__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/commands.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/custom.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/module__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/pkg_declare__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/setup.cfg +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/setup.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/mod_templates/test_service_scenario.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/breaking_change/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/breaking_change/markdown_template.jinja2 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/_macros.j2 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/cmdcov.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/component.css +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/component.js +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/favicon.ico +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/index.j2 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/index2.j2 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/cmdcov/module.j2 +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/command_change/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/command_change/custom.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/command_change/util.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/constant.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/extensions/version_upgrade.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/help/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/help/refdoc/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/help/refdoc/conf.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/latest_index.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/legal.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/data/cmd_example_config.json +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/linter.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rule_decorators.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/command_group_rules.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/command_rules.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/help_rules.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/rules/parameter_rules.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/linter/util.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/performance.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/pypi.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/python_sdk.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/regex.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/resource.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/secret.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/statistics/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/statistics/util.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/style.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/testtool/__init__.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/testtool/incremental_strategy.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/operations/testtool/profile_context.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/params.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/transformers.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/config.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/const.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/display.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/git_util.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/path.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/pypi.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/testing.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev/utilities/tools.py +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev.egg-info/dependency_links.txt +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev.egg-info/entry_points.txt +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/azdev.egg-info/top_level.txt +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/pyproject.toml +0 -0
- {azdev-0.2.10 → azdev-0.2.12}/setup.cfg +0 -0
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
Release History
|
|
4
4
|
===============
|
|
5
|
+
0.2.12
|
|
6
|
+
++++++
|
|
7
|
+
* Editable installs (``azdev setup``, ``azdev extension add``, code generation) now pass ``--no-build-isolation`` so wheels build against the environment's pinned ``setuptools`` instead of an isolated PEP 517 build environment. This fixes editable-install failures introduced by the ``setuptools`` pinning.
|
|
8
|
+
|
|
9
|
+
0.2.12b3
|
|
10
|
+
++++++++
|
|
11
|
+
* ``azdev extension update-index``/``publish``: Fix ``ValueError: Not a known wheel archive format`` when reading wheel metadata. The wheel is now downloaded with its ``.whl`` filename so ``pkginfo.Wheel`` can read it, and ``read_pkginfo`` falls back to a temporary ``.whl`` copy for any non-``.whl`` path. (#7740)
|
|
12
|
+
|
|
13
|
+
0.2.12b2
|
|
14
|
+
++++++++
|
|
15
|
+
* Quote paths when running editable installs (``pip install -e <path>``) and the pytest runner so that paths containing spaces (e.g. OneDrive folders) no longer break ``azdev extension add``, ``azdev setup``, code generation, and ``azdev test``. (#550, #415)
|
|
16
|
+
|
|
17
|
+
0.2.12b1
|
|
18
|
+
++++++++
|
|
19
|
+
* Extract extension metadata generation logic and decouple from ``wheel==0.30.0``; read wheel ``METADATA`` via ``pkginfo`` instead of the legacy ``metadata.json`` artifact. Drops the ``wheel==0.30.0`` and ``setuptools==70.0.0`` pins. (#521)
|
|
20
|
+
|
|
21
|
+
0.2.11
|
|
22
|
+
++++++
|
|
23
|
+
* `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
|
|
24
|
+
|
|
5
25
|
0.2.10
|
|
6
26
|
++++++
|
|
7
27
|
* Add support for Python 3.14 and drop support for Python 3.9
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: azdev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
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
|
|
@@ -38,9 +38,9 @@ Requires-Dist: jsbeautifier~=1.14.7
|
|
|
38
38
|
Requires-Dist: deepdiff~=8.6.1
|
|
39
39
|
Requires-Dist: azure-cli-diff-tool~=0.1.1
|
|
40
40
|
Requires-Dist: packaging
|
|
41
|
+
Requires-Dist: pkginfo
|
|
41
42
|
Requires-Dist: tqdm
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist: setuptools==70.0.0
|
|
43
|
+
Requires-Dist: setuptools<80,>=78.1.1
|
|
44
44
|
Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
|
|
45
45
|
Dynamic: author
|
|
46
46
|
Dynamic: author-email
|
|
@@ -203,6 +203,26 @@ License
|
|
|
203
203
|
|
|
204
204
|
Release History
|
|
205
205
|
===============
|
|
206
|
+
0.2.12
|
|
207
|
+
++++++
|
|
208
|
+
* Editable installs (``azdev setup``, ``azdev extension add``, code generation) now pass ``--no-build-isolation`` so wheels build against the environment's pinned ``setuptools`` instead of an isolated PEP 517 build environment. This fixes editable-install failures introduced by the ``setuptools`` pinning.
|
|
209
|
+
|
|
210
|
+
0.2.12b3
|
|
211
|
+
++++++++
|
|
212
|
+
* ``azdev extension update-index``/``publish``: Fix ``ValueError: Not a known wheel archive format`` when reading wheel metadata. The wheel is now downloaded with its ``.whl`` filename so ``pkginfo.Wheel`` can read it, and ``read_pkginfo`` falls back to a temporary ``.whl`` copy for any non-``.whl`` path. (#7740)
|
|
213
|
+
|
|
214
|
+
0.2.12b2
|
|
215
|
+
++++++++
|
|
216
|
+
* Quote paths when running editable installs (``pip install -e <path>``) and the pytest runner so that paths containing spaces (e.g. OneDrive folders) no longer break ``azdev extension add``, ``azdev setup``, code generation, and ``azdev test``. (#550, #415)
|
|
217
|
+
|
|
218
|
+
0.2.12b1
|
|
219
|
+
++++++++
|
|
220
|
+
* Extract extension metadata generation logic and decouple from ``wheel==0.30.0``; read wheel ``METADATA`` via ``pkginfo`` instead of the legacy ``metadata.json`` artifact. Drops the ``wheel==0.30.0`` and ``setuptools==70.0.0`` pins. (#521)
|
|
221
|
+
|
|
222
|
+
0.2.11
|
|
223
|
+
++++++
|
|
224
|
+
* `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
|
|
225
|
+
|
|
206
226
|
0.2.10
|
|
207
227
|
++++++
|
|
208
228
|
* Add support for Python 3.14 and drop support for Python 3.9
|
|
@@ -14,10 +14,12 @@ from knack.util import CLIError
|
|
|
14
14
|
|
|
15
15
|
from azdev.utilities import (
|
|
16
16
|
pip_cmd, display, heading, COMMAND_MODULE_PREFIX, EXTENSION_PREFIX, get_cli_repo_path, get_ext_repo_paths,
|
|
17
|
-
find_files)
|
|
17
|
+
find_files, quote_arg)
|
|
18
18
|
|
|
19
19
|
logger = get_logger(__name__)
|
|
20
20
|
|
|
21
|
+
_PIP_EDITABLE_OPTS = "--config-settings editable_mode=compat --no-build-isolation"
|
|
22
|
+
|
|
21
23
|
_MODULE_ROOT_PATH = os.path.join('src', 'azure-cli', 'azure', 'cli', 'command_modules')
|
|
22
24
|
|
|
23
25
|
|
|
@@ -297,6 +299,9 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d
|
|
|
297
299
|
_generate_files(env, kwargs, test_files, dest_path)
|
|
298
300
|
|
|
299
301
|
if is_ext:
|
|
300
|
-
result = pip_cmd(
|
|
302
|
+
result = pip_cmd(
|
|
303
|
+
'install -e {} {}'.format(quote_arg(new_package_path), _PIP_EDITABLE_OPTS),
|
|
304
|
+
"Installing `{}{}`...".format(prefix, name),
|
|
305
|
+
)
|
|
301
306
|
if result.error:
|
|
302
307
|
raise result.error # pylint: disable=raising-bad-type
|
|
@@ -16,11 +16,38 @@ from knack.util import CLIError
|
|
|
16
16
|
|
|
17
17
|
from azdev.utilities import (
|
|
18
18
|
cmd, py_cmd, pip_cmd, display, get_ext_repo_paths, find_files, get_azure_config, get_azdev_config,
|
|
19
|
-
require_azure_cli, heading, subheading, EXTENSION_PREFIX)
|
|
19
|
+
get_azure_config_dir, require_azure_cli, heading, subheading, quote_arg, EXTENSION_PREFIX)
|
|
20
20
|
from .version_upgrade import VersionUpgradeMod
|
|
21
21
|
|
|
22
22
|
logger = get_logger(__name__)
|
|
23
23
|
|
|
24
|
+
_PIP_EDITABLE_OPTS = "--config-settings editable_mode=compat --no-build-isolation"
|
|
25
|
+
|
|
26
|
+
# These are the index files cleared by CommandIndex().invalidate() in azure-cli-core.
|
|
27
|
+
# Refer: azure-cli-core/azure/cli/core/__init__.py
|
|
28
|
+
_COMMAND_INDEX_FILES = (
|
|
29
|
+
'commandIndex.json',
|
|
30
|
+
'extensionIndex.json',
|
|
31
|
+
'helpIndex.json',
|
|
32
|
+
'extensionHelpIndex.json',
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _invalidate_command_index():
|
|
37
|
+
"""Delete the CLI command index files so they are regenerated on next invocation.
|
|
38
|
+
|
|
39
|
+
This mirrors the behavior of ``CommandIndex().invalidate()`` in azure-cli-core but
|
|
40
|
+
works without requiring a fully initialized CLI session.
|
|
41
|
+
"""
|
|
42
|
+
azure_config_dir = get_azure_config_dir()
|
|
43
|
+
for filename in _COMMAND_INDEX_FILES:
|
|
44
|
+
filepath = os.path.join(azure_config_dir, filename)
|
|
45
|
+
try:
|
|
46
|
+
os.remove(filepath)
|
|
47
|
+
logger.debug("Deleted command index file: %s", filepath)
|
|
48
|
+
except OSError:
|
|
49
|
+
pass
|
|
50
|
+
|
|
24
51
|
|
|
25
52
|
def add_extension(extensions):
|
|
26
53
|
|
|
@@ -45,10 +72,15 @@ def add_extension(extensions):
|
|
|
45
72
|
raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
|
|
46
73
|
|
|
47
74
|
for path in paths_to_add:
|
|
48
|
-
result = pip_cmd(
|
|
75
|
+
result = pip_cmd(
|
|
76
|
+
'install -e {} {}'.format(quote_arg(path), _PIP_EDITABLE_OPTS),
|
|
77
|
+
"Adding extension '{}'...".format(path),
|
|
78
|
+
)
|
|
49
79
|
if result.error:
|
|
50
80
|
raise result.error # pylint: disable=raising-bad-type
|
|
51
81
|
|
|
82
|
+
_invalidate_command_index()
|
|
83
|
+
|
|
52
84
|
|
|
53
85
|
def remove_extension(extensions):
|
|
54
86
|
|
|
@@ -83,6 +115,8 @@ def remove_extension(extensions):
|
|
|
83
115
|
display("Removing '{}'...".format(path_to_remove))
|
|
84
116
|
shutil.rmtree(path_to_remove)
|
|
85
117
|
|
|
118
|
+
_invalidate_command_index()
|
|
119
|
+
|
|
86
120
|
|
|
87
121
|
def _get_installed_dev_extensions(dev_sources):
|
|
88
122
|
from glob import glob
|
|
@@ -254,7 +288,7 @@ def update_extension_index(extensions):
|
|
|
254
288
|
ext_dir = tempfile.mkdtemp(dir=extensions_dir)
|
|
255
289
|
whl_cache_dir = tempfile.mkdtemp()
|
|
256
290
|
whl_cache = {}
|
|
257
|
-
ext_file = get_whl_from_url(ext_path,
|
|
291
|
+
ext_file = get_whl_from_url(ext_path, ext_path.split("/")[-1], whl_cache_dir, whl_cache)
|
|
258
292
|
|
|
259
293
|
with open(index_path, 'r') as infile:
|
|
260
294
|
curr_index = json.loads(infile.read())
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# --------------------------------------------------------------------------------------------
|
|
2
|
+
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
# Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
# --------------------------------------------------------------------------------------------
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
Extension metadata extraction.
|
|
8
|
+
|
|
9
|
+
Replaces the legacy wheel-0.30.0 ``metadata.json`` read path with a
|
|
10
|
+
``pkginfo``-based reader of the spec-compliant ``METADATA`` file inside each
|
|
11
|
+
extension wheel, merged with the extension's ``azext_metadata.json``.
|
|
12
|
+
|
|
13
|
+
Used by ``azdev.operations.extensions.util.get_ext_metadata`` to build the
|
|
14
|
+
entries stored in ``index.json``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import json
|
|
20
|
+
import re
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Splits a requirement string into (name, spec). Accepts both shapes that may
|
|
26
|
+
# appear in METADATA Requires-Dist:
|
|
27
|
+
# * PEP 508 form: "oras==0.1.30" (modern setuptools / wheel)
|
|
28
|
+
# * PEP 314 form: "oras (==0.1.30)" (older setuptools, wheel 0.30.0)
|
|
29
|
+
# Either spec form is captured into a single, normalized spec string.
|
|
30
|
+
_REQ_SPLIT_RE = re.compile(
|
|
31
|
+
r"^\s*(?P<name>[A-Za-z0-9_.\-]+)\s*"
|
|
32
|
+
r"(?:\(\s*(?P<paren_spec>[^)]+?)\s*\)|(?P<bare_spec>[<>=!~].*?))?\s*$"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_extension_modname(ext_dir: Path) -> str:
|
|
37
|
+
pos = [d.name for d in ext_dir.iterdir() if d.is_dir() and d.name.startswith("azext_")]
|
|
38
|
+
if len(pos) != 1:
|
|
39
|
+
raise AssertionError(
|
|
40
|
+
"Expected exactly one azext_* module in {}, found: {}".format(ext_dir, pos)
|
|
41
|
+
)
|
|
42
|
+
return pos[0]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def read_azext_metadata(ext_dir: Path) -> Dict[str, Any]:
|
|
46
|
+
modname = _get_extension_modname(ext_dir)
|
|
47
|
+
path = ext_dir / modname / "azext_metadata.json"
|
|
48
|
+
if not path.is_file():
|
|
49
|
+
return {}
|
|
50
|
+
with path.open(encoding="utf-8") as fh:
|
|
51
|
+
return json.load(fh)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def pkginfo_to_dict(ext_file) -> Dict[str, Any]:
|
|
55
|
+
"""Build an index.json-shaped metadata dict from a wheel file.
|
|
56
|
+
|
|
57
|
+
This replaces the legacy ``metadata.json`` read path (which only existed
|
|
58
|
+
in wheels produced by ``wheel==0.30.0``) with a ``pkginfo.Wheel`` based
|
|
59
|
+
reader of the spec-defined ``METADATA`` file. Used by
|
|
60
|
+
``azdev.operations.extensions.util.get_ext_metadata``.
|
|
61
|
+
"""
|
|
62
|
+
return merge_to_index_metadata(read_pkginfo(Path(str(ext_file))), {})
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def read_pkginfo(wheel_path: Path) -> Dict[str, Any]:
|
|
66
|
+
"""Read spec-defined wheel metadata via pkginfo.Wheel."""
|
|
67
|
+
import pkginfo
|
|
68
|
+
|
|
69
|
+
wheel_path = Path(str(wheel_path))
|
|
70
|
+
target = wheel_path
|
|
71
|
+
if wheel_path.suffix != ".whl":
|
|
72
|
+
import shutil
|
|
73
|
+
import tempfile
|
|
74
|
+
target = Path(tempfile.mkdtemp()) / (wheel_path.name + ".whl")
|
|
75
|
+
shutil.copyfile(wheel_path, target)
|
|
76
|
+
|
|
77
|
+
whl = pkginfo.Wheel(str(target))
|
|
78
|
+
return {
|
|
79
|
+
"name": whl.name,
|
|
80
|
+
"version": whl.version,
|
|
81
|
+
"summary": whl.summary,
|
|
82
|
+
"description": whl.description,
|
|
83
|
+
"description_content_type": whl.description_content_type,
|
|
84
|
+
"license": whl.license,
|
|
85
|
+
"classifiers": list(whl.classifiers or []),
|
|
86
|
+
"requires_dist": list(whl.requires_dist or []),
|
|
87
|
+
"requires_python": whl.requires_python,
|
|
88
|
+
"author": whl.author,
|
|
89
|
+
"author_email": whl.author_email,
|
|
90
|
+
"home_page": whl.home_page,
|
|
91
|
+
"project_urls": list(whl.project_urls or []),
|
|
92
|
+
"metadata_version": whl.metadata_version,
|
|
93
|
+
"keywords": whl.keywords,
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _coerce_run_requires(requires_dist: List[str]) -> List[Dict[str, Any]]:
|
|
98
|
+
"""Approximate the legacy `run_requires` block produced by wheel 0.30.0.
|
|
99
|
+
|
|
100
|
+
Wheel 0.30.0 emitted each requirement in two forms inside `run_requires`:
|
|
101
|
+
* the PEP 314 / PEP 345 form: ``"oras (==0.1.30)"`` (name space then
|
|
102
|
+
version specifier wrapped in parentheses), and
|
|
103
|
+
* the canonical PEP 508 form: ``"oras==0.1.30"`` (no space, no parens).
|
|
104
|
+
|
|
105
|
+
It also sorted entries alphabetically by package name (this is observable in
|
|
106
|
+
`src/index.json`: every `run_requires` block is name-sorted regardless of
|
|
107
|
+
`install_requires` order in `setup.py`).
|
|
108
|
+
|
|
109
|
+
Modern wheel metadata (`METADATA` Requires-Dist) only carries PEP 508 and
|
|
110
|
+
preserves source order, so we reproduce both transformations here.
|
|
111
|
+
"""
|
|
112
|
+
if not requires_dist:
|
|
113
|
+
return []
|
|
114
|
+
|
|
115
|
+
parsed: List[Tuple[str, Optional[str], str]] = []
|
|
116
|
+
seen: set = set()
|
|
117
|
+
for req in requires_dist:
|
|
118
|
+
canonical = req.strip()
|
|
119
|
+
match = _REQ_SPLIT_RE.match(canonical)
|
|
120
|
+
if match:
|
|
121
|
+
name = match.group("name")
|
|
122
|
+
spec = match.group("paren_spec") or match.group("bare_spec")
|
|
123
|
+
spec = spec.strip() if spec else None
|
|
124
|
+
else:
|
|
125
|
+
name, spec = canonical, None
|
|
126
|
+
# Older setuptools (e.g. 70.0.0) writes Requires-Dist twice per
|
|
127
|
+
# package in METADATA -- once as "name (spec)" and once as
|
|
128
|
+
# "name==spec". Modern setuptools writes only the canonical PEP 508
|
|
129
|
+
# form. Deduplicate on (lowercase name, normalized spec) so the
|
|
130
|
+
# doubling step below produces the same output regardless of which
|
|
131
|
+
# setuptools generated the wheel.
|
|
132
|
+
key = (name.lower(), (spec or "").replace(" ", ""))
|
|
133
|
+
if key in seen:
|
|
134
|
+
continue
|
|
135
|
+
seen.add(key)
|
|
136
|
+
parsed.append((name, spec, canonical))
|
|
137
|
+
|
|
138
|
+
parsed.sort(key=lambda t: t[0].lower())
|
|
139
|
+
|
|
140
|
+
doubled: List[str] = []
|
|
141
|
+
for name, spec, canonical in parsed:
|
|
142
|
+
if spec:
|
|
143
|
+
doubled.append("{} ({})".format(name, spec))
|
|
144
|
+
doubled.append("{}{}".format(name, spec))
|
|
145
|
+
else:
|
|
146
|
+
doubled.append(canonical)
|
|
147
|
+
doubled.append(canonical)
|
|
148
|
+
return [{"requires": doubled}]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _coerce_project_urls(project_urls: List[str], home_page: Optional[str]) -> Dict[str, str]:
|
|
152
|
+
out: Dict[str, str] = {}
|
|
153
|
+
if home_page:
|
|
154
|
+
out["Home"] = home_page
|
|
155
|
+
for entry in project_urls or []:
|
|
156
|
+
if "," in entry:
|
|
157
|
+
label, url = entry.split(",", 1)
|
|
158
|
+
out[label.strip()] = url.strip()
|
|
159
|
+
return out
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _coerce_contacts(author: Optional[str], author_email: Optional[str]) -> List[Dict[str, str]]:
|
|
163
|
+
if not author and not author_email:
|
|
164
|
+
return []
|
|
165
|
+
contact: Dict[str, str] = {"role": "author"}
|
|
166
|
+
if author:
|
|
167
|
+
contact["name"] = author
|
|
168
|
+
if author_email:
|
|
169
|
+
contact["email"] = author_email
|
|
170
|
+
return [contact]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def merge_to_index_metadata(pkg: Dict[str, Any], azext: Dict[str, Any]) -> Dict[str, Any]:
|
|
174
|
+
"""Merge `pkginfo` output and `azext_metadata.json` into the index.json shape.
|
|
175
|
+
|
|
176
|
+
Precedence (highest first): azext_metadata > pkginfo > derived defaults.
|
|
177
|
+
"""
|
|
178
|
+
metadata: Dict[str, Any] = {}
|
|
179
|
+
|
|
180
|
+
metadata["name"] = pkg.get("name")
|
|
181
|
+
metadata["version"] = pkg.get("version")
|
|
182
|
+
metadata["summary"] = pkg.get("summary")
|
|
183
|
+
metadata["license"] = pkg.get("license")
|
|
184
|
+
metadata["metadata_version"] = pkg.get("metadata_version")
|
|
185
|
+
metadata["classifiers"] = pkg.get("classifiers") or []
|
|
186
|
+
metadata["extras"] = []
|
|
187
|
+
metadata["run_requires"] = _coerce_run_requires(pkg.get("requires_dist") or [])
|
|
188
|
+
metadata["requires_python"] = pkg.get("requires_python")
|
|
189
|
+
metadata["description_content_type"] = pkg.get("description_content_type")
|
|
190
|
+
|
|
191
|
+
contacts = _coerce_contacts(pkg.get("author"), pkg.get("author_email"))
|
|
192
|
+
project_urls = _coerce_project_urls(pkg.get("project_urls") or [], pkg.get("home_page"))
|
|
193
|
+
details: Dict[str, Any] = {}
|
|
194
|
+
if contacts:
|
|
195
|
+
details["contacts"] = contacts
|
|
196
|
+
if project_urls:
|
|
197
|
+
details["project_urls"] = project_urls
|
|
198
|
+
if details:
|
|
199
|
+
metadata["extensions"] = {"python.details": details}
|
|
200
|
+
|
|
201
|
+
metadata.update(azext)
|
|
202
|
+
|
|
203
|
+
return {k: v for k, v in metadata.items() if v is not None}
|
|
@@ -12,6 +12,7 @@ import zipfile
|
|
|
12
12
|
from knack.util import CLIError
|
|
13
13
|
|
|
14
14
|
from azdev.utilities import EXTENSION_PREFIX
|
|
15
|
+
from azdev.operations.extensions.metadata import pkginfo_to_dict
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
WHEEL_INFO_RE = re.compile(
|
|
@@ -45,7 +46,9 @@ def _get_azext_metadata(ext_dir):
|
|
|
45
46
|
|
|
46
47
|
def get_ext_metadata(ext_dir, ext_file, ext_name):
|
|
47
48
|
# Modification of https://github.com/Azure/azure-cli/blob/dev/src/azure-cli-core/azure/cli/core/extension.py#L89
|
|
48
|
-
|
|
49
|
+
# Read spec-defined wheel metadata via pkginfo so we don't depend on the
|
|
50
|
+
# legacy wheel-0.30.0 only ``metadata.json`` artifact.
|
|
51
|
+
generated_metadata = pkginfo_to_dict(ext_file)
|
|
49
52
|
with zipfile.ZipFile(ext_file, 'r') as zip_ref:
|
|
50
53
|
zip_ref.extractall(ext_dir)
|
|
51
54
|
metadata = {}
|
|
@@ -56,10 +59,7 @@ def get_ext_metadata(ext_dir, ext_file, ext_name):
|
|
|
56
59
|
for dist_info_dirname in dist_info_dirs:
|
|
57
60
|
parsed_dist_info_dir = WHEEL_INFO_RE(dist_info_dirname)
|
|
58
61
|
if parsed_dist_info_dir and parsed_dist_info_dir.groupdict().get('name') == ext_name.replace('-', '_'):
|
|
59
|
-
|
|
60
|
-
if os.path.isfile(whl_metadata_filepath):
|
|
61
|
-
with open(whl_metadata_filepath) as f:
|
|
62
|
-
metadata.update(json.load(f))
|
|
62
|
+
metadata.update(generated_metadata)
|
|
63
63
|
return metadata
|
|
64
64
|
|
|
65
65
|
|
|
@@ -17,10 +17,12 @@ from azdev.operations.extensions import (
|
|
|
17
17
|
from azdev.params import Flag
|
|
18
18
|
from azdev.utilities import (
|
|
19
19
|
display, heading, subheading, pip_cmd, CommandError, find_file,
|
|
20
|
-
get_azdev_config_dir, get_azdev_config, require_virtual_env, get_azure_config)
|
|
20
|
+
get_azdev_config_dir, get_azdev_config, require_virtual_env, get_azure_config, quote_arg)
|
|
21
21
|
|
|
22
22
|
logger = get_logger(__name__)
|
|
23
23
|
|
|
24
|
+
_PIP_EDITABLE_OPTS = "--config-settings editable_mode=compat --no-build-isolation"
|
|
25
|
+
|
|
24
26
|
|
|
25
27
|
def _check_path(path, file_name):
|
|
26
28
|
""" Ensures the file_name is provided in the supplied path. """
|
|
@@ -49,7 +51,8 @@ def _install_extensions(ext_paths):
|
|
|
49
51
|
|
|
50
52
|
# install specified extensions
|
|
51
53
|
for path in ext_paths or []:
|
|
52
|
-
result = pip_cmd('install -e {}
|
|
54
|
+
result = pip_cmd('install -e {} {}'.format(quote_arg(path), _PIP_EDITABLE_OPTS),
|
|
55
|
+
"Adding extension '{}'...".format(path))
|
|
53
56
|
if result.error:
|
|
54
57
|
raise result.error # pylint: disable=raising-bad-type
|
|
55
58
|
|
|
@@ -75,13 +78,13 @@ def _install_cli(cli_path, deps=None):
|
|
|
75
78
|
privates_dir = os.path.join(cli_path, "privates")
|
|
76
79
|
if os.path.isdir(privates_dir) and os.listdir(privates_dir):
|
|
77
80
|
whl_list = " ".join(
|
|
78
|
-
[os.path.join(privates_dir, f) for f in os.listdir(privates_dir)]
|
|
81
|
+
[quote_arg(os.path.join(privates_dir, f)) for f in os.listdir(privates_dir)]
|
|
79
82
|
)
|
|
80
83
|
pip_cmd("install {}".format(whl_list), "Installing private whl files...")
|
|
81
84
|
|
|
82
85
|
# install general requirements
|
|
83
86
|
pip_cmd(
|
|
84
|
-
"install -r {}".format(os.path.join(cli_path, "requirements.txt")),
|
|
87
|
+
"install -r {}".format(quote_arg(os.path.join(cli_path, "requirements.txt"))),
|
|
85
88
|
"Installing `requirements.txt`..."
|
|
86
89
|
)
|
|
87
90
|
|
|
@@ -90,38 +93,41 @@ def _install_cli(cli_path, deps=None):
|
|
|
90
93
|
# Resolve dependencies from setup.py files.
|
|
91
94
|
# command modules have dependency on azure-cli-core so install this first
|
|
92
95
|
pip_cmd(
|
|
93
|
-
"install -e {}".format(os.path.join(cli_src, 'azure-cli-telemetry')),
|
|
96
|
+
"install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-telemetry')), _PIP_EDITABLE_OPTS),
|
|
94
97
|
"Installing `azure-cli-telemetry`..."
|
|
95
98
|
)
|
|
96
99
|
pip_cmd(
|
|
97
|
-
"install -e {}".format(os.path.join(cli_src, 'azure-cli-core')),
|
|
100
|
+
"install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-core')), _PIP_EDITABLE_OPTS),
|
|
98
101
|
"Installing `azure-cli-core`..."
|
|
99
102
|
)
|
|
100
103
|
|
|
101
104
|
# azure cli has dependencies on the above packages so install this one last
|
|
102
105
|
pip_cmd(
|
|
103
|
-
"install -e {}".format(os.path.join(cli_src, 'azure-cli')),
|
|
106
|
+
"install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli')), _PIP_EDITABLE_OPTS),
|
|
104
107
|
"Installing `azure-cli`..."
|
|
105
108
|
)
|
|
106
109
|
|
|
107
110
|
pip_cmd(
|
|
108
|
-
"install -e {}".format(os.path.join(cli_src, 'azure-cli-testsdk')),
|
|
111
|
+
"install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-testsdk')), _PIP_EDITABLE_OPTS),
|
|
109
112
|
"Installing `azure-cli-testsdk`..."
|
|
110
113
|
)
|
|
111
114
|
else:
|
|
112
115
|
# First install packages without dependencies,
|
|
113
116
|
# then resolve dependencies from requirements.*.txt file.
|
|
114
117
|
pip_cmd(
|
|
115
|
-
"install -e {} --no-deps".format(
|
|
118
|
+
"install -e {} --no-deps {}".format(
|
|
119
|
+
quote_arg(os.path.join(cli_src, 'azure-cli-telemetry')), _PIP_EDITABLE_OPTS),
|
|
116
120
|
"Installing `azure-cli-telemetry`..."
|
|
117
121
|
)
|
|
118
122
|
pip_cmd(
|
|
119
|
-
"install -e {} --no-deps".format(
|
|
123
|
+
"install -e {} --no-deps {}".format(
|
|
124
|
+
quote_arg(os.path.join(cli_src, 'azure-cli-core')), _PIP_EDITABLE_OPTS),
|
|
120
125
|
"Installing `azure-cli-core`..."
|
|
121
126
|
)
|
|
122
127
|
|
|
123
128
|
pip_cmd(
|
|
124
|
-
"install -e {} --no-deps".format(
|
|
129
|
+
"install -e {} --no-deps {}".format(
|
|
130
|
+
quote_arg(os.path.join(cli_src, 'azure-cli')), _PIP_EDITABLE_OPTS),
|
|
125
131
|
"Installing `azure-cli`..."
|
|
126
132
|
)
|
|
127
133
|
|
|
@@ -129,14 +135,14 @@ def _install_cli(cli_path, deps=None):
|
|
|
129
135
|
# azure-cli package for running commands.
|
|
130
136
|
# Here we need to install with dependencies for azdev test.
|
|
131
137
|
pip_cmd(
|
|
132
|
-
"install -e {}".format(os.path.join(cli_src, 'azure-cli-testsdk')),
|
|
138
|
+
"install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-testsdk')), _PIP_EDITABLE_OPTS),
|
|
133
139
|
"Installing `azure-cli-testsdk`..."
|
|
134
140
|
)
|
|
135
141
|
import platform
|
|
136
142
|
system = platform.system()
|
|
137
143
|
req_file = 'requirements.py3.{}.txt'.format(system)
|
|
138
144
|
pip_cmd(
|
|
139
|
-
"install -r {}".format(os.path.join(cli_src, 'azure-cli', req_file)),
|
|
145
|
+
"install -r {}".format(quote_arg(os.path.join(cli_src, 'azure-cli', req_file))),
|
|
140
146
|
"Installing `{}`...".format(req_file)
|
|
141
147
|
)
|
|
142
148
|
|
|
@@ -9,7 +9,7 @@ import sys
|
|
|
9
9
|
|
|
10
10
|
from knack.log import get_logger
|
|
11
11
|
|
|
12
|
-
from azdev.utilities import call
|
|
12
|
+
from azdev.utilities import call, quote_arg
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def get_test_runner(parallel, log_path, last_failed, no_exit_first, mark):
|
|
@@ -18,10 +18,11 @@ def get_test_runner(parallel, log_path, last_failed, no_exit_first, mark):
|
|
|
18
18
|
|
|
19
19
|
logger = get_logger(__name__)
|
|
20
20
|
|
|
21
|
+
quoted_log_path = quote_arg(log_path)
|
|
21
22
|
if os.name == 'posix':
|
|
22
|
-
arguments = ['-x', '-v', '--forked', '-p no:warnings', '--log-level=WARN', '--junit-xml',
|
|
23
|
+
arguments = ['-x', '-v', '--forked', '-p no:warnings', '--log-level=WARN', '--junit-xml', quoted_log_path]
|
|
23
24
|
else:
|
|
24
|
-
arguments = ['-x', '-v', '-p no:warnings', '--log-level=WARN', '--junit-xml',
|
|
25
|
+
arguments = ['-x', '-v', '-p no:warnings', '--log-level=WARN', '--junit-xml', quoted_log_path]
|
|
25
26
|
|
|
26
27
|
if no_exit_first:
|
|
27
28
|
arguments.remove('-x')
|
|
@@ -29,7 +30,7 @@ def get_test_runner(parallel, log_path, last_failed, no_exit_first, mark):
|
|
|
29
30
|
if mark:
|
|
30
31
|
arguments.append('-m "{}"'.format(mark))
|
|
31
32
|
|
|
32
|
-
arguments.extend(test_paths)
|
|
33
|
+
arguments.extend(quote_arg(test_path) for test_path in test_paths)
|
|
33
34
|
if parallel:
|
|
34
35
|
arguments += ['-n', 'auto']
|
|
35
36
|
if last_failed:
|
|
@@ -15,6 +15,7 @@ from .command import (
|
|
|
15
15
|
cmd,
|
|
16
16
|
py_cmd,
|
|
17
17
|
pip_cmd,
|
|
18
|
+
quote_arg,
|
|
18
19
|
CommandError
|
|
19
20
|
)
|
|
20
21
|
from .const import (
|
|
@@ -71,6 +72,7 @@ __all__ = [
|
|
|
71
72
|
'cmd',
|
|
72
73
|
'py_cmd',
|
|
73
74
|
'pip_cmd',
|
|
75
|
+
'quote_arg',
|
|
74
76
|
'CommandError',
|
|
75
77
|
'test_cmd',
|
|
76
78
|
'get_env_path',
|
|
@@ -15,6 +15,26 @@ from knack.util import CommandResultItem
|
|
|
15
15
|
logger = get_logger(__name__)
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
def quote_arg(arg):
|
|
19
|
+
""" Quote a single command-line argument so it survives the shell/argv parsing
|
|
20
|
+
performed by the command runners in this module.
|
|
21
|
+
|
|
22
|
+
On Windows, command strings are passed verbatim to ``subprocess`` (and ultimately
|
|
23
|
+
``CreateProcess``), so Windows-style quoting via ``subprocess.list2cmdline`` is used.
|
|
24
|
+
On POSIX, command strings are split with ``shlex.split``, so POSIX-style quoting via
|
|
25
|
+
``shlex.quote`` is used. This makes paths containing spaces (e.g. OneDrive folders)
|
|
26
|
+
safe to interpolate into command strings.
|
|
27
|
+
|
|
28
|
+
:param arg: The argument to quote.
|
|
29
|
+
:returns: (str) the quoted argument.
|
|
30
|
+
"""
|
|
31
|
+
from azdev.utilities import IS_WINDOWS
|
|
32
|
+
arg = str(arg)
|
|
33
|
+
if IS_WINDOWS:
|
|
34
|
+
return subprocess.list2cmdline([arg])
|
|
35
|
+
return shlex.quote(arg)
|
|
36
|
+
|
|
37
|
+
|
|
18
38
|
class CommandError(Exception):
|
|
19
39
|
|
|
20
40
|
def __init__(self, output, exit_code, command):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: azdev
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.12
|
|
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
|
|
@@ -38,9 +38,9 @@ Requires-Dist: jsbeautifier~=1.14.7
|
|
|
38
38
|
Requires-Dist: deepdiff~=8.6.1
|
|
39
39
|
Requires-Dist: azure-cli-diff-tool~=0.1.1
|
|
40
40
|
Requires-Dist: packaging
|
|
41
|
+
Requires-Dist: pkginfo
|
|
41
42
|
Requires-Dist: tqdm
|
|
42
|
-
Requires-Dist:
|
|
43
|
-
Requires-Dist: setuptools==70.0.0
|
|
43
|
+
Requires-Dist: setuptools<80,>=78.1.1
|
|
44
44
|
Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
|
|
45
45
|
Dynamic: author
|
|
46
46
|
Dynamic: author-email
|
|
@@ -203,6 +203,26 @@ License
|
|
|
203
203
|
|
|
204
204
|
Release History
|
|
205
205
|
===============
|
|
206
|
+
0.2.12
|
|
207
|
+
++++++
|
|
208
|
+
* Editable installs (``azdev setup``, ``azdev extension add``, code generation) now pass ``--no-build-isolation`` so wheels build against the environment's pinned ``setuptools`` instead of an isolated PEP 517 build environment. This fixes editable-install failures introduced by the ``setuptools`` pinning.
|
|
209
|
+
|
|
210
|
+
0.2.12b3
|
|
211
|
+
++++++++
|
|
212
|
+
* ``azdev extension update-index``/``publish``: Fix ``ValueError: Not a known wheel archive format`` when reading wheel metadata. The wheel is now downloaded with its ``.whl`` filename so ``pkginfo.Wheel`` can read it, and ``read_pkginfo`` falls back to a temporary ``.whl`` copy for any non-``.whl`` path. (#7740)
|
|
213
|
+
|
|
214
|
+
0.2.12b2
|
|
215
|
+
++++++++
|
|
216
|
+
* Quote paths when running editable installs (``pip install -e <path>``) and the pytest runner so that paths containing spaces (e.g. OneDrive folders) no longer break ``azdev extension add``, ``azdev setup``, code generation, and ``azdev test``. (#550, #415)
|
|
217
|
+
|
|
218
|
+
0.2.12b1
|
|
219
|
+
++++++++
|
|
220
|
+
* Extract extension metadata generation logic and decouple from ``wheel==0.30.0``; read wheel ``METADATA`` via ``pkginfo`` instead of the legacy ``metadata.json`` artifact. Drops the ``wheel==0.30.0`` and ``setuptools==70.0.0`` pins. (#521)
|
|
221
|
+
|
|
222
|
+
0.2.11
|
|
223
|
+
++++++
|
|
224
|
+
* `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
|
|
225
|
+
|
|
206
226
|
0.2.10
|
|
207
227
|
++++++
|
|
208
228
|
* Add support for Python 3.14 and drop support for Python 3.9
|
|
@@ -67,6 +67,7 @@ azdev/operations/command_change/__init__.py
|
|
|
67
67
|
azdev/operations/command_change/custom.py
|
|
68
68
|
azdev/operations/command_change/util.py
|
|
69
69
|
azdev/operations/extensions/__init__.py
|
|
70
|
+
azdev/operations/extensions/metadata.py
|
|
70
71
|
azdev/operations/extensions/util.py
|
|
71
72
|
azdev/operations/extensions/version_upgrade.py
|
|
72
73
|
azdev/operations/help/__init__.py
|
|
@@ -85,9 +85,12 @@ setup(
|
|
|
85
85
|
'deepdiff~=8.6.1',
|
|
86
86
|
'azure-cli-diff-tool~=0.1.1',
|
|
87
87
|
'packaging',
|
|
88
|
+
'pkginfo',
|
|
88
89
|
'tqdm',
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
# setuptools >= 78.1.1 to address CVE-2025-47273 (path traversal).
|
|
91
|
+
# Upper bound < 80 keeps legacy `setup.py develop` invocations working
|
|
92
|
+
# for extensions that haven't migrated to pyproject.toml yet.
|
|
93
|
+
'setuptools>=78.1.1,<80',
|
|
91
94
|
'microsoft-security-utilities-secret-masker~=1.0.0b4'
|
|
92
95
|
],
|
|
93
96
|
package_data={
|
|
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
|
|
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
|
|
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
|
|
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
|