azdev 0.2.10__tar.gz → 0.2.13__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.
Files changed (107) hide show
  1. {azdev-0.2.10 → azdev-0.2.13}/HISTORY.rst +26 -0
  2. {azdev-0.2.10/azdev.egg-info → azdev-0.2.13}/PKG-INFO +30 -3
  3. {azdev-0.2.10 → azdev-0.2.13}/azdev/__init__.py +1 -1
  4. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/code_gen.py +9 -2
  5. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/extensions/__init__.py +62 -3
  6. azdev-0.2.13/azdev/operations/extensions/metadata.py +203 -0
  7. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/extensions/util.py +5 -5
  8. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/setup.py +21 -14
  9. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/testtool/pytest_runner.py +5 -4
  10. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/__init__.py +2 -0
  11. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/command.py +20 -0
  12. {azdev-0.2.10 → azdev-0.2.13/azdev.egg-info}/PKG-INFO +30 -3
  13. {azdev-0.2.10 → azdev-0.2.13}/azdev.egg-info/SOURCES.txt +1 -0
  14. {azdev-0.2.10 → azdev-0.2.13}/azdev.egg-info/requires.txt +3 -2
  15. {azdev-0.2.10 → azdev-0.2.13}/setup.py +9 -2
  16. {azdev-0.2.10 → azdev-0.2.13}/LICENSE +0 -0
  17. {azdev-0.2.10 → azdev-0.2.13}/MANIFEST.in +0 -0
  18. {azdev-0.2.10 → azdev-0.2.13}/README.md +0 -0
  19. {azdev-0.2.10 → azdev-0.2.13}/README.rst +0 -0
  20. {azdev-0.2.10 → azdev-0.2.13}/azdev/__main__.py +0 -0
  21. {azdev-0.2.10 → azdev-0.2.13}/azdev/commands.py +0 -0
  22. {azdev-0.2.10 → azdev-0.2.13}/azdev/completer.py +0 -0
  23. {azdev-0.2.10 → azdev-0.2.13}/azdev/config/__init__.py +0 -0
  24. {azdev-0.2.10 → azdev-0.2.13}/azdev/config/cli.flake8 +0 -0
  25. {azdev-0.2.10 → azdev-0.2.13}/azdev/config/cli_pylintrc +0 -0
  26. {azdev-0.2.10 → azdev-0.2.13}/azdev/config/ext.flake8 +0 -0
  27. {azdev-0.2.10 → azdev-0.2.13}/azdev/config/ext_pylintrc +0 -0
  28. {azdev-0.2.10 → azdev-0.2.13}/azdev/help.py +0 -0
  29. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/HISTORY.rst +0 -0
  30. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/README.rst +0 -0
  31. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/_client_factory.py +0 -0
  32. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/_help.py +0 -0
  33. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/_params.py +0 -0
  34. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/_validators.py +0 -0
  35. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/azext_metadata.json +0 -0
  36. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/blank__init__.py +0 -0
  37. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/commands.py +0 -0
  38. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/custom.py +0 -0
  39. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/module__init__.py +0 -0
  40. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/pkg_declare__init__.py +0 -0
  41. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/setup.cfg +0 -0
  42. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/setup.py +0 -0
  43. {azdev-0.2.10 → azdev-0.2.13}/azdev/mod_templates/test_service_scenario.py +0 -0
  44. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/__init__.py +0 -0
  45. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/breaking_change/__init__.py +0 -0
  46. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/breaking_change/markdown_template.jinja2 +0 -0
  47. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/__init__.py +0 -0
  48. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/_macros.j2 +0 -0
  49. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/cmdcov.py +0 -0
  50. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/component.css +0 -0
  51. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/component.js +0 -0
  52. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/favicon.ico +0 -0
  53. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/index.j2 +0 -0
  54. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/index2.j2 +0 -0
  55. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/cmdcov/module.j2 +0 -0
  56. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/command_change/__init__.py +0 -0
  57. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/command_change/custom.py +0 -0
  58. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/command_change/util.py +0 -0
  59. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/constant.py +0 -0
  60. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/extensions/version_upgrade.py +0 -0
  61. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/help/__init__.py +0 -0
  62. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/help/refdoc/__init__.py +0 -0
  63. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/help/refdoc/conf.py +0 -0
  64. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/latest_index.py +0 -0
  65. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/legal.py +0 -0
  66. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/__init__.py +0 -0
  67. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/data/cmd_example_config.json +0 -0
  68. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/linter.py +0 -0
  69. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
  70. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
  71. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rule_decorators.py +0 -0
  72. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/__init__.py +0 -0
  73. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
  74. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
  75. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/command_group_rules.py +0 -0
  76. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/command_rules.py +0 -0
  77. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/help_rules.py +0 -0
  78. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
  79. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/rules/parameter_rules.py +0 -0
  80. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/linter/util.py +0 -0
  81. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/performance.py +0 -0
  82. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/pypi.py +0 -0
  83. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/python_sdk.py +0 -0
  84. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/regex.py +0 -0
  85. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/resource.py +0 -0
  86. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/secret.py +0 -0
  87. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/statistics/__init__.py +0 -0
  88. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/statistics/util.py +0 -0
  89. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/style.py +0 -0
  90. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/testtool/__init__.py +0 -0
  91. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/testtool/incremental_strategy.py +0 -0
  92. {azdev-0.2.10 → azdev-0.2.13}/azdev/operations/testtool/profile_context.py +0 -0
  93. {azdev-0.2.10 → azdev-0.2.13}/azdev/params.py +0 -0
  94. {azdev-0.2.10 → azdev-0.2.13}/azdev/transformers.py +0 -0
  95. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/config.py +0 -0
  96. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/const.py +0 -0
  97. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/display.py +0 -0
  98. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/git_util.py +0 -0
  99. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/path.py +0 -0
  100. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/pypi.py +0 -0
  101. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/testing.py +0 -0
  102. {azdev-0.2.10 → azdev-0.2.13}/azdev/utilities/tools.py +0 -0
  103. {azdev-0.2.10 → azdev-0.2.13}/azdev.egg-info/dependency_links.txt +0 -0
  104. {azdev-0.2.10 → azdev-0.2.13}/azdev.egg-info/entry_points.txt +0 -0
  105. {azdev-0.2.10 → azdev-0.2.13}/azdev.egg-info/top_level.txt +0 -0
  106. {azdev-0.2.10 → azdev-0.2.13}/pyproject.toml +0 -0
  107. {azdev-0.2.10 → azdev-0.2.13}/setup.cfg +0 -0
@@ -2,6 +2,32 @@
2
2
 
3
3
  Release History
4
4
  ===============
5
+ 0.2.13
6
+ ++++++
7
+ * `azdev extension add` / `azdev setup`: regenerate `*.egg-info` after the editable install so development extensions stay discoverable. `--no-build-isolation` (0.2.12) means pip no longer leaves an `*.egg-info` in the source tree, which made `azdev extension add` a silent no-op for discovery. (#554)
8
+ * `azdev extension build`: depend on `wheel` so `setup.py bdist_wheel` works even when the environment's `setuptools` predates the bundled `bdist_wheel` command (< 70.1). Modern, unpinned `wheel` for building only; does not restore the `wheel==0.30.0` metadata pin removed in 0.2.12b1.
9
+ * `azdev extension build`: surface the underlying `setup.py` output on build failure instead of re-raising an opaque `CalledProcessError`.
10
+
11
+ 0.2.12
12
+ ++++++
13
+ * 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.
14
+
15
+ 0.2.12b3
16
+ ++++++++
17
+ * `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)
18
+
19
+ 0.2.12b2
20
+ ++++++++
21
+ * 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)
22
+
23
+ 0.2.12b1
24
+ ++++++++
25
+ * 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)
26
+
27
+ 0.2.11
28
+ ++++++
29
+ * `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
30
+
5
31
  0.2.10
6
32
  ++++++
7
33
  * 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.10
3
+ Version: 0.2.13
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,10 @@ 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: wheel==0.30.0
43
- Requires-Dist: setuptools==70.0.0
43
+ Requires-Dist: setuptools<80,>=78.1.1
44
+ Requires-Dist: wheel
44
45
  Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
45
46
  Dynamic: author
46
47
  Dynamic: author-email
@@ -203,6 +204,32 @@ License
203
204
 
204
205
  Release History
205
206
  ===============
207
+ 0.2.13
208
+ ++++++
209
+ * `azdev extension add` / `azdev setup`: regenerate `*.egg-info` after the editable install so development extensions stay discoverable. `--no-build-isolation` (0.2.12) means pip no longer leaves an `*.egg-info` in the source tree, which made `azdev extension add` a silent no-op for discovery. (#554)
210
+ * `azdev extension build`: depend on `wheel` so `setup.py bdist_wheel` works even when the environment's `setuptools` predates the bundled `bdist_wheel` command (< 70.1). Modern, unpinned `wheel` for building only; does not restore the `wheel==0.30.0` metadata pin removed in 0.2.12b1.
211
+ * `azdev extension build`: surface the underlying `setup.py` output on build failure instead of re-raising an opaque `CalledProcessError`.
212
+
213
+ 0.2.12
214
+ ++++++
215
+ * 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.
216
+
217
+ 0.2.12b3
218
+ ++++++++
219
+ * `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)
220
+
221
+ 0.2.12b2
222
+ ++++++++
223
+ * 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)
224
+
225
+ 0.2.12b1
226
+ ++++++++
227
+ * 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)
228
+
229
+ 0.2.11
230
+ ++++++
231
+ * `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
232
+
206
233
  0.2.10
207
234
  ++++++
208
235
  * Add support for Python 3.14 and drop support for Python 3.9
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __VERSION__ = '0.2.10'
7
+ __VERSION__ = '0.2.13'
@@ -14,10 +14,13 @@ 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
+ from azdev.operations.extensions import _ensure_egg_info
18
19
 
19
20
  logger = get_logger(__name__)
20
21
 
22
+ _PIP_EDITABLE_OPTS = "--config-settings editable_mode=compat --no-build-isolation"
23
+
21
24
  _MODULE_ROOT_PATH = os.path.join('src', 'azure-cli', 'azure', 'cli', 'command_modules')
22
25
 
23
26
 
@@ -297,6 +300,10 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d
297
300
  _generate_files(env, kwargs, test_files, dest_path)
298
301
 
299
302
  if is_ext:
300
- result = pip_cmd('install -e {}'.format(new_package_path), "Installing `{}{}`...".format(prefix, name))
303
+ result = pip_cmd(
304
+ 'install -e {} {}'.format(quote_arg(new_package_path), _PIP_EDITABLE_OPTS),
305
+ "Installing `{}{}`...".format(prefix, name),
306
+ )
301
307
  if result.error:
302
308
  raise result.error # pylint: disable=raising-bad-type
309
+ _ensure_egg_info(new_package_path)
@@ -16,11 +16,55 @@ 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
+
51
+
52
+ def _ensure_egg_info(path):
53
+ # editable_mode=compat + --no-build-isolation results in a PEP 660 editable install:
54
+ # the .dist-info goes to site-packages and no .egg-info is left in the source tree.
55
+ # azdev finds dev extensions by globbing *.egg-info under the repo, so without it the
56
+ # extension is invisible even though pip succeeded. Regenerate it with the env's setuptools.
57
+ if find_files(path, '*.egg-info'):
58
+ return
59
+ result = py_cmd(
60
+ 'setup.py egg_info',
61
+ "Generating metadata for '{}'...".format(path),
62
+ is_module=False,
63
+ cwd=path,
64
+ )
65
+ if result.error:
66
+ raise result.error # pylint: disable=raising-bad-type
67
+
24
68
 
25
69
  def add_extension(extensions):
26
70
 
@@ -45,9 +89,15 @@ def add_extension(extensions):
45
89
  raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
46
90
 
47
91
  for path in paths_to_add:
48
- result = pip_cmd('install -e {}'.format(path), "Adding extension '{}'...".format(path))
92
+ result = pip_cmd(
93
+ 'install -e {} {}'.format(quote_arg(path), _PIP_EDITABLE_OPTS),
94
+ "Adding extension '{}'...".format(path),
95
+ )
49
96
  if result.error:
50
97
  raise result.error # pylint: disable=raising-bad-type
98
+ _ensure_egg_info(path)
99
+
100
+ _invalidate_command_index()
51
101
 
52
102
 
53
103
  def remove_extension(extensions):
@@ -83,6 +133,8 @@ def remove_extension(extensions):
83
133
  display("Removing '{}'...".format(path_to_remove))
84
134
  shutil.rmtree(path_to_remove)
85
135
 
136
+ _invalidate_command_index()
137
+
86
138
 
87
139
  def _get_installed_dev_extensions(dev_sources):
88
140
  from glob import glob
@@ -254,7 +306,7 @@ def update_extension_index(extensions):
254
306
  ext_dir = tempfile.mkdtemp(dir=extensions_dir)
255
307
  whl_cache_dir = tempfile.mkdtemp()
256
308
  whl_cache = {}
257
- ext_file = get_whl_from_url(ext_path, extension_name, whl_cache_dir, whl_cache)
309
+ ext_file = get_whl_from_url(ext_path, ext_path.split("/")[-1], whl_cache_dir, whl_cache)
258
310
 
259
311
  with open(index_path, 'r') as infile:
260
312
  curr_index = json.loads(infile.read())
@@ -302,6 +354,13 @@ def build_extensions(extensions, dist_dir='dist'):
302
354
  command = 'setup.py bdist_wheel -b bdist -d {}'.format(dist_dir)
303
355
  result = py_cmd(command, "Building extension '{}'...".format(path), is_module=False)
304
356
  if result.error:
357
+ # py_cmd captures the build output; surface it so the real setup.py failure is visible
358
+ # instead of only the opaque CalledProcessError.
359
+ build_output = result.output
360
+ if isinstance(build_output, (bytes, bytearray)):
361
+ build_output = build_output.decode('utf-8', 'ignore')
362
+ if build_output:
363
+ logger.error(build_output)
305
364
  raise result.error # pylint: disable=raising-bad-type
306
365
  os.chdir(original_cwd)
307
366
 
@@ -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
- WHL_METADATA_FILENAME = 'metadata.json'
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
- whl_metadata_filepath = os.path.join(ext_dir, dist_info_dirname, WHL_METADATA_FILENAME)
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
 
@@ -13,14 +13,16 @@ from knack.log import get_logger
13
13
  from knack.util import CLIError
14
14
 
15
15
  from azdev.operations.extensions import (
16
- list_extensions, add_extension_repo, remove_extension)
16
+ list_extensions, add_extension_repo, remove_extension, _ensure_egg_info)
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,9 +51,11 @@ 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 {}'.format(path), "Adding extension '{}'...".format(path))
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
58
+ _ensure_egg_info(path)
55
59
 
56
60
 
57
61
  def _install_cli(cli_path, deps=None):
@@ -75,13 +79,13 @@ def _install_cli(cli_path, deps=None):
75
79
  privates_dir = os.path.join(cli_path, "privates")
76
80
  if os.path.isdir(privates_dir) and os.listdir(privates_dir):
77
81
  whl_list = " ".join(
78
- [os.path.join(privates_dir, f) for f in os.listdir(privates_dir)]
82
+ [quote_arg(os.path.join(privates_dir, f)) for f in os.listdir(privates_dir)]
79
83
  )
80
84
  pip_cmd("install {}".format(whl_list), "Installing private whl files...")
81
85
 
82
86
  # install general requirements
83
87
  pip_cmd(
84
- "install -r {}".format(os.path.join(cli_path, "requirements.txt")),
88
+ "install -r {}".format(quote_arg(os.path.join(cli_path, "requirements.txt"))),
85
89
  "Installing `requirements.txt`..."
86
90
  )
87
91
 
@@ -90,38 +94,41 @@ def _install_cli(cli_path, deps=None):
90
94
  # Resolve dependencies from setup.py files.
91
95
  # command modules have dependency on azure-cli-core so install this first
92
96
  pip_cmd(
93
- "install -e {}".format(os.path.join(cli_src, 'azure-cli-telemetry')),
97
+ "install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-telemetry')), _PIP_EDITABLE_OPTS),
94
98
  "Installing `azure-cli-telemetry`..."
95
99
  )
96
100
  pip_cmd(
97
- "install -e {}".format(os.path.join(cli_src, 'azure-cli-core')),
101
+ "install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-core')), _PIP_EDITABLE_OPTS),
98
102
  "Installing `azure-cli-core`..."
99
103
  )
100
104
 
101
105
  # azure cli has dependencies on the above packages so install this one last
102
106
  pip_cmd(
103
- "install -e {}".format(os.path.join(cli_src, 'azure-cli')),
107
+ "install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli')), _PIP_EDITABLE_OPTS),
104
108
  "Installing `azure-cli`..."
105
109
  )
106
110
 
107
111
  pip_cmd(
108
- "install -e {}".format(os.path.join(cli_src, 'azure-cli-testsdk')),
112
+ "install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-testsdk')), _PIP_EDITABLE_OPTS),
109
113
  "Installing `azure-cli-testsdk`..."
110
114
  )
111
115
  else:
112
116
  # First install packages without dependencies,
113
117
  # then resolve dependencies from requirements.*.txt file.
114
118
  pip_cmd(
115
- "install -e {} --no-deps".format(os.path.join(cli_src, 'azure-cli-telemetry')),
119
+ "install -e {} --no-deps {}".format(
120
+ quote_arg(os.path.join(cli_src, 'azure-cli-telemetry')), _PIP_EDITABLE_OPTS),
116
121
  "Installing `azure-cli-telemetry`..."
117
122
  )
118
123
  pip_cmd(
119
- "install -e {} --no-deps".format(os.path.join(cli_src, 'azure-cli-core')),
124
+ "install -e {} --no-deps {}".format(
125
+ quote_arg(os.path.join(cli_src, 'azure-cli-core')), _PIP_EDITABLE_OPTS),
120
126
  "Installing `azure-cli-core`..."
121
127
  )
122
128
 
123
129
  pip_cmd(
124
- "install -e {} --no-deps".format(os.path.join(cli_src, 'azure-cli')),
130
+ "install -e {} --no-deps {}".format(
131
+ quote_arg(os.path.join(cli_src, 'azure-cli')), _PIP_EDITABLE_OPTS),
125
132
  "Installing `azure-cli`..."
126
133
  )
127
134
 
@@ -129,14 +136,14 @@ def _install_cli(cli_path, deps=None):
129
136
  # azure-cli package for running commands.
130
137
  # Here we need to install with dependencies for azdev test.
131
138
  pip_cmd(
132
- "install -e {}".format(os.path.join(cli_src, 'azure-cli-testsdk')),
139
+ "install -e {} {}".format(quote_arg(os.path.join(cli_src, 'azure-cli-testsdk')), _PIP_EDITABLE_OPTS),
133
140
  "Installing `azure-cli-testsdk`..."
134
141
  )
135
142
  import platform
136
143
  system = platform.system()
137
144
  req_file = 'requirements.py3.{}.txt'.format(system)
138
145
  pip_cmd(
139
- "install -r {}".format(os.path.join(cli_src, 'azure-cli', req_file)),
146
+ "install -r {}".format(quote_arg(os.path.join(cli_src, 'azure-cli', req_file))),
140
147
  "Installing `{}`...".format(req_file)
141
148
  )
142
149
 
@@ -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', log_path]
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', log_path]
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.10
3
+ Version: 0.2.13
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,10 @@ 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: wheel==0.30.0
43
- Requires-Dist: setuptools==70.0.0
43
+ Requires-Dist: setuptools<80,>=78.1.1
44
+ Requires-Dist: wheel
44
45
  Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
45
46
  Dynamic: author
46
47
  Dynamic: author-email
@@ -203,6 +204,32 @@ License
203
204
 
204
205
  Release History
205
206
  ===============
207
+ 0.2.13
208
+ ++++++
209
+ * `azdev extension add` / `azdev setup`: regenerate `*.egg-info` after the editable install so development extensions stay discoverable. `--no-build-isolation` (0.2.12) means pip no longer leaves an `*.egg-info` in the source tree, which made `azdev extension add` a silent no-op for discovery. (#554)
210
+ * `azdev extension build`: depend on `wheel` so `setup.py bdist_wheel` works even when the environment's `setuptools` predates the bundled `bdist_wheel` command (< 70.1). Modern, unpinned `wheel` for building only; does not restore the `wheel==0.30.0` metadata pin removed in 0.2.12b1.
211
+ * `azdev extension build`: surface the underlying `setup.py` output on build failure instead of re-raising an opaque `CalledProcessError`.
212
+
213
+ 0.2.12
214
+ ++++++
215
+ * 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.
216
+
217
+ 0.2.12b3
218
+ ++++++++
219
+ * `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)
220
+
221
+ 0.2.12b2
222
+ ++++++++
223
+ * 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)
224
+
225
+ 0.2.12b1
226
+ ++++++++
227
+ * 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)
228
+
229
+ 0.2.11
230
+ ++++++
231
+ * `azdev extension add/remove`: Invalidate command index after installing or removing extensions.
232
+
206
233
  0.2.10
207
234
  ++++++
208
235
  * 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
@@ -16,7 +16,8 @@ jsbeautifier~=1.14.7
16
16
  deepdiff~=8.6.1
17
17
  azure-cli-diff-tool~=0.1.1
18
18
  packaging
19
+ pkginfo
19
20
  tqdm
20
- wheel==0.30.0
21
- setuptools==70.0.0
21
+ setuptools<80,>=78.1.1
22
+ wheel
22
23
  microsoft-security-utilities-secret-masker~=1.0.0b4
@@ -85,9 +85,16 @@ 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
- 'wheel==0.30.0',
90
- 'setuptools==70.0.0',
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',
94
+ # `azdev extension build` runs `setup.py bdist_wheel`; keep the wheel command available even
95
+ # when the environment's setuptools predates the bundled bdist_wheel (< 70.1). Modern, unpinned
96
+ # wheel for building only -- not the `wheel==0.30.0` metadata pin dropped in 0.2.12b1.
97
+ 'wheel',
91
98
  'microsoft-security-utilities-secret-masker~=1.0.0b4'
92
99
  ],
93
100
  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