azdev 0.2.12__tar.gz → 0.2.14__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 (110) hide show
  1. {azdev-0.2.12 → azdev-0.2.14}/HISTORY.rst +28 -4
  2. {azdev-0.2.12/azdev.egg-info → azdev-0.2.14}/PKG-INFO +38 -23
  3. {azdev-0.2.12 → azdev-0.2.14}/azdev/__init__.py +1 -1
  4. azdev-0.2.14/azdev/mod_templates/pyproject.toml +49 -0
  5. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/__init__.py +1 -2
  6. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/cmdcov.py +1 -2
  7. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/code_gen.py +3 -3
  8. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/command_change/__init__.py +3 -14
  9. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/command_change/custom.py +28 -0
  10. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/extensions/__init__.py +24 -16
  11. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/extensions/metadata.py +21 -4
  12. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/latest_index.py +1 -4
  13. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/pypi.py +56 -50
  14. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/regex.py +1 -2
  15. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/setup.py +2 -1
  16. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/statistics/__init__.py +3 -14
  17. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/__init__.py +12 -0
  18. azdev-0.2.14/azdev/utilities/packaging.py +184 -0
  19. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/path.py +3 -2
  20. {azdev-0.2.12 → azdev-0.2.14/azdev.egg-info}/PKG-INFO +38 -23
  21. {azdev-0.2.12 → azdev-0.2.14}/azdev.egg-info/SOURCES.txt +2 -3
  22. {azdev-0.2.12 → azdev-0.2.14}/azdev.egg-info/requires.txt +7 -2
  23. azdev-0.2.14/pyproject.toml +111 -0
  24. azdev-0.2.12/azdev/mod_templates/setup.cfg +0 -0
  25. azdev-0.2.12/azdev/mod_templates/setup.py +0 -60
  26. azdev-0.2.12/pyproject.toml +0 -9
  27. azdev-0.2.12/setup.py +0 -108
  28. {azdev-0.2.12 → azdev-0.2.14}/LICENSE +0 -0
  29. {azdev-0.2.12 → azdev-0.2.14}/MANIFEST.in +0 -0
  30. {azdev-0.2.12 → azdev-0.2.14}/README.md +0 -0
  31. {azdev-0.2.12 → azdev-0.2.14}/README.rst +0 -0
  32. {azdev-0.2.12 → azdev-0.2.14}/azdev/__main__.py +0 -0
  33. {azdev-0.2.12 → azdev-0.2.14}/azdev/commands.py +0 -0
  34. {azdev-0.2.12 → azdev-0.2.14}/azdev/completer.py +0 -0
  35. {azdev-0.2.12 → azdev-0.2.14}/azdev/config/__init__.py +0 -0
  36. {azdev-0.2.12 → azdev-0.2.14}/azdev/config/cli.flake8 +0 -0
  37. {azdev-0.2.12 → azdev-0.2.14}/azdev/config/cli_pylintrc +0 -0
  38. {azdev-0.2.12 → azdev-0.2.14}/azdev/config/ext.flake8 +0 -0
  39. {azdev-0.2.12 → azdev-0.2.14}/azdev/config/ext_pylintrc +0 -0
  40. {azdev-0.2.12 → azdev-0.2.14}/azdev/help.py +0 -0
  41. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/HISTORY.rst +0 -0
  42. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/README.rst +0 -0
  43. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/_client_factory.py +0 -0
  44. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/_help.py +0 -0
  45. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/_params.py +0 -0
  46. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/_validators.py +0 -0
  47. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/azext_metadata.json +0 -0
  48. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/blank__init__.py +0 -0
  49. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/commands.py +0 -0
  50. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/custom.py +0 -0
  51. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/module__init__.py +0 -0
  52. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/pkg_declare__init__.py +0 -0
  53. {azdev-0.2.12 → azdev-0.2.14}/azdev/mod_templates/test_service_scenario.py +0 -0
  54. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/__init__.py +0 -0
  55. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/breaking_change/__init__.py +0 -0
  56. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/breaking_change/markdown_template.jinja2 +0 -0
  57. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/_macros.j2 +0 -0
  58. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/component.css +0 -0
  59. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/component.js +0 -0
  60. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/favicon.ico +0 -0
  61. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/index.j2 +0 -0
  62. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/index2.j2 +0 -0
  63. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/cmdcov/module.j2 +0 -0
  64. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/command_change/util.py +0 -0
  65. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/constant.py +0 -0
  66. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/extensions/util.py +0 -0
  67. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/extensions/version_upgrade.py +0 -0
  68. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/help/__init__.py +0 -0
  69. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/help/refdoc/__init__.py +0 -0
  70. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/help/refdoc/conf.py +0 -0
  71. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/legal.py +0 -0
  72. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/__init__.py +0 -0
  73. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/data/cmd_example_config.json +0 -0
  74. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/linter.py +0 -0
  75. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/pylint_checkers/__init__.py +0 -0
  76. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/pylint_checkers/show_command.py +0 -0
  77. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rule_decorators.py +0 -0
  78. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/__init__.py +0 -0
  79. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/ci_exclusions.yml +0 -0
  80. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/command_coverage_rules.py +0 -0
  81. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/command_group_rules.py +0 -0
  82. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/command_rules.py +0 -0
  83. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/help_rules.py +0 -0
  84. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/linter_exclusions.yml +0 -0
  85. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/rules/parameter_rules.py +0 -0
  86. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/linter/util.py +0 -0
  87. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/performance.py +0 -0
  88. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/python_sdk.py +0 -0
  89. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/resource.py +0 -0
  90. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/secret.py +0 -0
  91. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/statistics/util.py +0 -0
  92. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/style.py +0 -0
  93. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/testtool/__init__.py +0 -0
  94. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/testtool/incremental_strategy.py +0 -0
  95. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/testtool/profile_context.py +0 -0
  96. {azdev-0.2.12 → azdev-0.2.14}/azdev/operations/testtool/pytest_runner.py +0 -0
  97. {azdev-0.2.12 → azdev-0.2.14}/azdev/params.py +0 -0
  98. {azdev-0.2.12 → azdev-0.2.14}/azdev/transformers.py +0 -0
  99. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/command.py +0 -0
  100. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/config.py +0 -0
  101. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/const.py +0 -0
  102. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/display.py +0 -0
  103. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/git_util.py +0 -0
  104. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/pypi.py +0 -0
  105. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/testing.py +0 -0
  106. {azdev-0.2.12 → azdev-0.2.14}/azdev/utilities/tools.py +0 -0
  107. {azdev-0.2.12 → azdev-0.2.14}/azdev.egg-info/dependency_links.txt +0 -0
  108. {azdev-0.2.12 → azdev-0.2.14}/azdev.egg-info/entry_points.txt +0 -0
  109. {azdev-0.2.12 → azdev-0.2.14}/azdev.egg-info/top_level.txt +0 -0
  110. {azdev-0.2.12 → azdev-0.2.14}/setup.cfg +0 -0
@@ -2,21 +2,45 @@
2
2
 
3
3
  Release History
4
4
  ===============
5
+ 0.2.14
6
+ ++++++
7
+ * `azdev extension add`/`list`/`build`: discover extensions by packaging configuration instead of globbing for `setup.py`, so extensions that have migrated to `pyproject.toml` are found. Previously a migrated extension was reported as `extension(s) not found`.
8
+ * `azdev extension build`: build through the PEP 517 frontend for `pyproject.toml` extensions, keeping `setup.py bdist_wheel` for the rest. `--no-isolation` preserves the previous behaviour for extensions whose build imports their own `azext_*` package.
9
+ * `azdev extension add`/`azdev setup`: generate `*.egg-info` without `setup.py`, so `az` can still discover a migrated extension as a development extension. `azure-cli-core` finds dev extensions by globbing `*.egg-info`.
10
+ * `azdev extension create`: scaffold `pyproject.toml` instead of `setup.py` and `setup.cfg`.
11
+ * `azdev extension update-index`: keep the home page under the `Home` label for `pyproject.toml` extensions, which declare `[project.urls] Homepage` rather than `url=`, so `index.json` entries do not change shape on migration. Also accept `License-Expression` in place of `License`.
12
+ * `azdev extension build`: fix the build-failure handler reading `CommandResultItem.output`, which does not exist. A failed build raised `AttributeError` instead of showing the build log.
13
+ * Migrate azdev and `azure-cli-diff-tool` to PEP 621 `pyproject.toml`. Both `setup.py` files are gone; wheel contents are unchanged.
14
+ * Build with `python -m build` in CI, and check metadata with `twine check` instead of `setup.py check -r -s`.
15
+ * Drop the `setuptools<80` cap. It guarded `setup.py develop`, which azdev stopped calling in 0.2.12. The `>=78.1.1` floor for CVE-2025-47273 stays.
16
+ * Fix `azdev cli check-versions` running `python -m setup.py bdist_wheel`, which could never work.
17
+ * Fix `azure-cli-diff-tool`'s sdist missing `HISTORY.rst`, which broke building a wheel from it.
18
+ * Fix `scripts/ci/extract_version.sh` producing an empty version.
19
+ * Set `Description-Content-Type` and stop reading the long description through `codecs.open`, which wrote `\r\r\n` line endings on Windows.
20
+ * `azdev command-change meta-export`: record `action` for parameters that take no value. Flag actions are written as `store_true`, `store_false`, `store_const` or `count`, including preview, experimental and deprecated arguments, whose action knack wraps in a subclass. Other actions registered with `nargs=0`, such as the one for `--acquire-policy-token`, are written as `store_true`. Readers of the metadata can now tell a flag such as `--enable-fips` from an option that takes a value. Other parameters get no `action` key, and `meta-diff` doesn't compare it.
21
+ * `azdev test`: cap `pytest` below 9.2. On Linux and macOS `azdev test` runs pytest with `--forked`, and pytest-forked 1.7.5 can't serialise pytest 9.2 reports, so every test failed with "EXITED with status 3". The cap stays until pytest-forked supports pytest 9.2 (pytest-dev/pytest-forked#113).
22
+
23
+ 0.2.13
24
+ ++++++
25
+ * `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)
26
+ * `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.
27
+ * `azdev extension build`: surface the underlying `setup.py` output on build failure instead of re-raising an opaque `CalledProcessError`.
28
+
5
29
  0.2.12
6
30
  ++++++
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.
31
+ * 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
32
 
9
33
  0.2.12b3
10
34
  ++++++++
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)
35
+ * `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
36
 
13
37
  0.2.12b2
14
38
  ++++++++
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)
39
+ * 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
40
 
17
41
  0.2.12b1
18
42
  ++++++++
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)
43
+ * 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
44
 
21
45
  0.2.11
22
46
  ++++++
@@ -1,17 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: azdev
3
- Version: 0.2.12
3
+ Version: 0.2.14
4
4
  Summary: Microsoft Azure CLI Developer Tools
5
- Home-page: https://github.com/Azure/azure-cli-dev-tools
6
- Author: Microsoft Corporation
7
- Author-email: azpycli@microsoft.com
8
- License: MIT
5
+ Author-email: Microsoft Corporation <azpycli@microsoft.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Azure/azure-cli-dev-tools
9
8
  Keywords: azure
10
9
  Classifier: Development Status :: 5 - Production/Stable
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Topic :: Software Development :: Build Tools
13
12
  Classifier: Environment :: Console
14
- Classifier: License :: OSI Approved :: MIT License
15
13
  Classifier: Natural Language :: English
16
14
  Classifier: Programming Language :: Python :: 3.10
17
15
  Classifier: Programming Language :: Python :: 3.11
@@ -19,8 +17,10 @@ Classifier: Programming Language :: Python :: 3.12
19
17
  Classifier: Programming Language :: Python :: 3.13
20
18
  Classifier: Programming Language :: Python :: 3.14
21
19
  Requires-Python: >=3.10
20
+ Description-Content-Type: text/x-rst
22
21
  License-File: LICENSE
23
22
  Requires-Dist: azure-multiapi-storage
23
+ Requires-Dist: build
24
24
  Requires-Dist: docutils
25
25
  Requires-Dist: flake8
26
26
  Requires-Dist: gitpython
@@ -29,7 +29,7 @@ Requires-Dist: knack
29
29
  Requires-Dist: pylint<5,>=4
30
30
  Requires-Dist: pytest-xdist
31
31
  Requires-Dist: pytest-forked
32
- Requires-Dist: pytest>=5.0.0
32
+ Requires-Dist: pytest<9.2,>=5.0.0
33
33
  Requires-Dist: pyyaml
34
34
  Requires-Dist: requests
35
35
  Requires-Dist: sphinx==1.6.7
@@ -39,20 +39,12 @@ Requires-Dist: deepdiff~=8.6.1
39
39
  Requires-Dist: azure-cli-diff-tool~=0.1.1
40
40
  Requires-Dist: packaging
41
41
  Requires-Dist: pkginfo
42
+ Requires-Dist: tomli; python_version < "3.11"
42
43
  Requires-Dist: tqdm
43
- Requires-Dist: setuptools<80,>=78.1.1
44
+ Requires-Dist: setuptools>=78.1.1
45
+ Requires-Dist: wheel
44
46
  Requires-Dist: microsoft-security-utilities-secret-masker~=1.0.0b4
45
- Dynamic: author
46
- Dynamic: author-email
47
- Dynamic: classifier
48
- Dynamic: description
49
- Dynamic: home-page
50
- Dynamic: keywords
51
- Dynamic: license
52
47
  Dynamic: license-file
53
- Dynamic: requires-dist
54
- Dynamic: requires-python
55
- Dynamic: summary
56
48
 
57
49
  Microsoft Azure CLI Dev Tools (azdev)
58
50
  =====================================
@@ -198,26 +190,49 @@ License
198
190
 
199
191
  THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.::
200
192
 
201
-
202
193
  .. :changelog:
203
194
 
204
195
  Release History
205
196
  ===============
197
+ 0.2.14
198
+ ++++++
199
+ * `azdev extension add`/`list`/`build`: discover extensions by packaging configuration instead of globbing for `setup.py`, so extensions that have migrated to `pyproject.toml` are found. Previously a migrated extension was reported as `extension(s) not found`.
200
+ * `azdev extension build`: build through the PEP 517 frontend for `pyproject.toml` extensions, keeping `setup.py bdist_wheel` for the rest. `--no-isolation` preserves the previous behaviour for extensions whose build imports their own `azext_*` package.
201
+ * `azdev extension add`/`azdev setup`: generate `*.egg-info` without `setup.py`, so `az` can still discover a migrated extension as a development extension. `azure-cli-core` finds dev extensions by globbing `*.egg-info`.
202
+ * `azdev extension create`: scaffold `pyproject.toml` instead of `setup.py` and `setup.cfg`.
203
+ * `azdev extension update-index`: keep the home page under the `Home` label for `pyproject.toml` extensions, which declare `[project.urls] Homepage` rather than `url=`, so `index.json` entries do not change shape on migration. Also accept `License-Expression` in place of `License`.
204
+ * `azdev extension build`: fix the build-failure handler reading `CommandResultItem.output`, which does not exist. A failed build raised `AttributeError` instead of showing the build log.
205
+ * Migrate azdev and `azure-cli-diff-tool` to PEP 621 `pyproject.toml`. Both `setup.py` files are gone; wheel contents are unchanged.
206
+ * Build with `python -m build` in CI, and check metadata with `twine check` instead of `setup.py check -r -s`.
207
+ * Drop the `setuptools<80` cap. It guarded `setup.py develop`, which azdev stopped calling in 0.2.12. The `>=78.1.1` floor for CVE-2025-47273 stays.
208
+ * Fix `azdev cli check-versions` running `python -m setup.py bdist_wheel`, which could never work.
209
+ * Fix `azure-cli-diff-tool`'s sdist missing `HISTORY.rst`, which broke building a wheel from it.
210
+ * Fix `scripts/ci/extract_version.sh` producing an empty version.
211
+ * Set `Description-Content-Type` and stop reading the long description through `codecs.open`, which wrote `\r\r\n` line endings on Windows.
212
+ * `azdev command-change meta-export`: record `action` for parameters that take no value. Flag actions are written as `store_true`, `store_false`, `store_const` or `count`, including preview, experimental and deprecated arguments, whose action knack wraps in a subclass. Other actions registered with `nargs=0`, such as the one for `--acquire-policy-token`, are written as `store_true`. Readers of the metadata can now tell a flag such as `--enable-fips` from an option that takes a value. Other parameters get no `action` key, and `meta-diff` doesn't compare it.
213
+ * `azdev test`: cap `pytest` below 9.2. On Linux and macOS `azdev test` runs pytest with `--forked`, and pytest-forked 1.7.5 can't serialise pytest 9.2 reports, so every test failed with "EXITED with status 3". The cap stays until pytest-forked supports pytest 9.2 (pytest-dev/pytest-forked#113).
214
+
215
+ 0.2.13
216
+ ++++++
217
+ * `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)
218
+ * `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.
219
+ * `azdev extension build`: surface the underlying `setup.py` output on build failure instead of re-raising an opaque `CalledProcessError`.
220
+
206
221
  0.2.12
207
222
  ++++++
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.
223
+ * 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
224
 
210
225
  0.2.12b3
211
226
  ++++++++
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)
227
+ * `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
228
 
214
229
  0.2.12b2
215
230
  ++++++++
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)
231
+ * 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
232
 
218
233
  0.2.12b1
219
234
  ++++++++
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)
235
+ * 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
236
 
222
237
  0.2.11
223
238
  ++++++
@@ -4,4 +4,4 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
- __VERSION__ = '0.2.12'
7
+ __VERSION__ = '0.2.14'
@@ -0,0 +1,49 @@
1
+ [build-system]
2
+ requires = ["setuptools>=70.1.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "{{ pkg_name }}"
7
+ # TODO: Confirm this is the right version number you want and it matches your
8
+ # HISTORY.rst entry.
9
+ version = "0.1.0"
10
+ description = "Microsoft Azure Command-Line Tools {{ display_name }} {{ 'Extension' if is_ext else 'Command Module' }}"
11
+ # TODO: Update author and email, if applicable
12
+ authors = [{ name = "Microsoft Corporation", email = "azpycli@microsoft.com" }]
13
+ license = { text = "MIT" }
14
+ # The full list of classifiers is available at
15
+ # https://pypi.python.org/pypi?%3Aaction=list_classifiers
16
+ classifiers = [
17
+ "Development Status :: 4 - Beta",
18
+ "Intended Audience :: Developers",
19
+ "Intended Audience :: System Administrators",
20
+ "Programming Language :: Python",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "License :: OSI Approved :: MIT License",
27
+ ]
28
+ # TODO: Add any additional SDK dependencies here
29
+ dependencies = {% if dependencies %}[
30
+ {{ dependencies|join(',\n '|safe) }}
31
+ ]{% else %}[]{% endif %}
32
+ # `readme` is the concatenation of README.rst and HISTORY.rst, which PEP 621
33
+ # cannot express statically.
34
+ dynamic = ["readme"]
35
+
36
+ [project.urls]
37
+ # TODO: change to your extension source code repo if the code will not be put in azure-cli-extensions repo
38
+ Homepage = "https://github.com/Azure/{% if is_ext %}azure-cli-extensions/tree/main/src/{{ pkg_name }}{% else %}azure-cli{% endif %}"
39
+
40
+ [tool.setuptools.dynamic]
41
+ readme = { file = ["README.rst", "HISTORY.rst"], content-type = "text/x-rst" }
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["."]
45
+ exclude = ["tests*"]
46
+ {% if is_ext %}
47
+ [tool.setuptools.package-data]
48
+ {{ ext_long_name }} = ["azext_metadata.json"]
49
+ {% endif %}
@@ -21,8 +21,7 @@ logger = get_logger(__name__)
21
21
  try:
22
22
  with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file:
23
23
  config = yaml.safe_load(file)
24
- # pylint: disable=broad-exception-caught
25
- except Exception:
24
+ except Exception: # pylint: disable=broad-exception-caught
26
25
  url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml"
27
26
  response = requests.get(url)
28
27
  config = yaml.safe_load(response.text)
@@ -22,8 +22,7 @@ logger = get_logger(__name__)
22
22
  try:
23
23
  with open(os.path.join(get_cli_repo_path(), 'scripts', 'ci', 'cmdcov.yml'), 'r') as file:
24
24
  config = yaml.safe_load(file)
25
- # pylint: disable=broad-exception-caught
26
- except Exception:
25
+ except Exception: # pylint: disable=broad-exception-caught
27
26
  url = "https://raw.githubusercontent.com/Azure/azure-cli/dev/scripts/ci/cmdcov.yml"
28
27
  response = requests.get(url)
29
28
  config = yaml.safe_load(response.text)
@@ -15,6 +15,7 @@ from knack.util import CLIError
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
17
  find_files, quote_arg)
18
+ from azdev.operations.extensions import _ensure_egg_info
18
19
 
19
20
  logger = get_logger(__name__)
20
21
 
@@ -29,7 +30,6 @@ def _ensure_dir(path):
29
30
 
30
31
 
31
32
  def _generate_files(env, generation_kwargs, file_list, dest_path):
32
-
33
33
  # allow sending a single item
34
34
  if not isinstance(file_list, list):
35
35
  file_list = [file_list]
@@ -268,8 +268,7 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d
268
268
  root_files = [
269
269
  'HISTORY.rst',
270
270
  'README.rst',
271
- 'setup.cfg',
272
- 'setup.py'
271
+ 'pyproject.toml'
273
272
  ]
274
273
  _generate_files(env, kwargs, root_files, dest_path)
275
274
 
@@ -305,3 +304,4 @@ def _create_package(prefix, repo_path, is_ext, name='test', display_name=None, d
305
304
  )
306
305
  if result.error:
307
306
  raise result.error # pylint: disable=raising-bad-type
307
+ _ensure_egg_info(new_package_path)
@@ -10,8 +10,9 @@ import time
10
10
 
11
11
  from knack.log import get_logger
12
12
  import azure_cli_diff_tool
13
- from azdev.utilities import display, require_azure_cli, heading, get_path_table, filter_by_git_diff, \
13
+ from azdev.utilities import display, require_azure_cli, heading, filter_by_git_diff, \
14
14
  calc_selected_mod_names
15
+ from azdev.utilities.path import calc_selected_modules
15
16
  from .custom import DiffExportFormat, get_commands_meta, STORED_DEPRECATION_KEY
16
17
  from .util import export_commands_meta, dump_command_tree, add_to_command_tree
17
18
  from ..statistics import _create_invoker_and_load_cmds, _get_command_source, \
@@ -32,19 +33,7 @@ def export_command_meta(modules=None, git_source=None, git_target=None, git_repo
32
33
  meta_output_path=None):
33
34
  require_azure_cli()
34
35
 
35
- # allow user to run only on CLI or extensions
36
- cli_only = modules == ['CLI']
37
- ext_only = modules == ['EXT']
38
- if cli_only or ext_only:
39
- modules = None
40
-
41
- selected_modules = get_path_table(include_only=modules, include_whl_extensions=include_whl_extensions)
42
-
43
- if cli_only:
44
- selected_modules['ext'] = {}
45
- if ext_only:
46
- selected_modules['core'] = {}
47
- selected_modules['mod'] = {}
36
+ selected_modules = calc_selected_modules(modules, include_whl_extensions=include_whl_extensions)
48
37
 
49
38
  # filter down to only modules that have changed based on git diff
50
39
  selected_modules = filter_by_git_diff(selected_modules, git_source, git_target, git_repo)
@@ -4,6 +4,7 @@
4
4
  # license information.
5
5
  # -----------------------------------------------------------------------------
6
6
 
7
+ import argparse
7
8
  from enum import Enum
8
9
 
9
10
  from knack.log import get_logger
@@ -13,6 +14,17 @@ logger = get_logger(__name__)
13
14
 
14
15
  STORED_DEPRECATION_KEY = ["expiration", "target", "redirect", "hide"]
15
16
 
17
+ # actions that take no value, so a reader of the metadata can tell a flag from an option that needs one
18
+ FLAG_ACTIONS = ["store_true", "store_false", "store_const", "count"]
19
+ # knack wraps the action in a subclass for preview, experimental and deprecated arguments,
20
+ # so match classes by base class, checking subclasses before their bases
21
+ FLAG_ACTION_CLASSES = [
22
+ (argparse._StoreTrueAction, "store_true"), # pylint: disable=protected-access
23
+ (argparse._StoreFalseAction, "store_false"), # pylint: disable=protected-access
24
+ (argparse._StoreConstAction, "store_const"), # pylint: disable=protected-access
25
+ (argparse._CountAction, "count"), # pylint: disable=protected-access
26
+ ]
27
+
16
28
 
17
29
  class DiffExportFormat(Enum):
18
30
  DICT = "dict"
@@ -98,6 +110,21 @@ def process_arg_type(argument_settings, para):
98
110
  para["type"] = raw_type if raw_type in ["str", "int", "float", "bool", "file_type"] else "custom_type"
99
111
 
100
112
 
113
+ def process_arg_action(argument_settings, para):
114
+ action = argument_settings.get("action", None)
115
+ if isinstance(action, str):
116
+ if action in FLAG_ACTIONS:
117
+ para["action"] = action
118
+ elif isinstance(action, type):
119
+ for action_class, action_name in FLAG_ACTION_CLASSES:
120
+ if issubclass(action, action_class):
121
+ para["action"] = action_name
122
+ break
123
+ # a custom action that takes no value, such as the one for --acquire-policy-token
124
+ if "action" not in para and argument_settings.get("nargs", None) == 0:
125
+ para["action"] = "store_true"
126
+
127
+
101
128
  def normalize_para_types(para):
102
129
  type_string_opts = ["string", "str", "aazstrarg",
103
130
  "aazresourcelocationarg", "aazresourcegroupnamearg", "aazresourceidarg",
@@ -159,6 +186,7 @@ def gen_command_meta(command_info, with_help=False, with_example=False):
159
186
  process_arg_options(settings, para)
160
187
  process_arg_options_deprecation(settings, para)
161
188
  process_arg_type(settings, para)
189
+ process_arg_action(settings, para)
162
190
  if settings.get("required", False):
163
191
  para["required"] = True
164
192
  if settings.get("choices", None):
@@ -15,8 +15,9 @@ from knack.prompting import prompt_y_n
15
15
  from knack.util import CLIError
16
16
 
17
17
  from azdev.utilities import (
18
- cmd, py_cmd, pip_cmd, display, get_ext_repo_paths, find_files, get_azure_config, get_azdev_config,
19
- get_azure_config_dir, require_azure_cli, heading, subheading, quote_arg, EXTENSION_PREFIX)
18
+ cmd, pip_cmd, display, get_ext_repo_paths, find_files, get_azure_config, get_azdev_config,
19
+ get_azure_config_dir, require_azure_cli, heading, subheading, quote_arg, EXTENSION_PREFIX,
20
+ build_package_wheel, find_package_configs_recursive, generate_egg_info)
20
21
  from .version_upgrade import VersionUpgradeMod
21
22
 
22
23
  logger = get_logger(__name__)
@@ -49,12 +50,24 @@ def _invalidate_command_index():
49
50
  pass
50
51
 
51
52
 
53
+ def _ensure_egg_info(path):
54
+ # editable_mode=compat + --no-build-isolation results in a PEP 660 editable install:
55
+ # the .dist-info goes to site-packages and no .egg-info is left in the source tree.
56
+ # azdev finds dev extensions by globbing *.egg-info under the repo, so without it the
57
+ # extension is invisible even though pip succeeded. Regenerate it with the env's setuptools.
58
+ if find_files(path, '*.egg-info'):
59
+ return
60
+ result = generate_egg_info(path, "Generating metadata for '{}'...".format(path))
61
+ if result.error:
62
+ raise result.error # pylint: disable=raising-bad-type
63
+
64
+
52
65
  def add_extension(extensions):
53
66
 
54
67
  ext_paths = get_ext_repo_paths()
55
68
  if not ext_paths or ext_paths == ['_NONE_']:
56
69
  raise CLIError('Extension repo path is empty. Please try `azdev extension repo add` to add an extension repo')
57
- all_extensions = find_files(ext_paths, 'setup.py')
70
+ all_extensions = find_package_configs_recursive(ext_paths)
58
71
 
59
72
  if extensions == ['*']:
60
73
  paths_to_add = [os.path.dirname(path) for path in all_extensions
@@ -78,6 +91,7 @@ def add_extension(extensions):
78
91
  )
79
92
  if result.error:
80
93
  raise result.error # pylint: disable=raising-bad-type
94
+ _ensure_egg_info(path)
81
95
 
82
96
  _invalidate_command_index()
83
97
 
@@ -150,7 +164,7 @@ def list_extensions():
150
164
  installed_names = [x['name'] for x in installed]
151
165
  results = []
152
166
 
153
- for ext_path in find_files(dev_sources, 'setup.py'):
167
+ for ext_path in find_package_configs_recursive(dev_sources):
154
168
  # skip non-extension packages that may be in the extension folder (for example, from a virtual environment)
155
169
  try:
156
170
  glob_pattern = os.path.join(os.path.split(ext_path)[0], '{}*'.format(EXTENSION_PREFIX))
@@ -283,9 +297,8 @@ def update_extension_index(extensions):
283
297
  except IndexError:
284
298
  raise CLIError('unable to parse extension name')
285
299
 
286
- # TODO: Update this!
287
- extensions_dir = tempfile.mkdtemp()
288
- ext_dir = tempfile.mkdtemp(dir=extensions_dir)
300
+ # Create temporary directories for extraction and caching
301
+ ext_dir = tempfile.mkdtemp()
289
302
  whl_cache_dir = tempfile.mkdtemp()
290
303
  whl_cache = {}
291
304
  ext_file = get_whl_from_url(ext_path, ext_path.split("/")[-1], whl_cache_dir, whl_cache)
@@ -316,7 +329,7 @@ def build_extensions(extensions, dist_dir='dist'):
316
329
  ext_paths = get_ext_repo_paths()
317
330
  if not ext_paths or ext_paths == ['_NONE_']:
318
331
  raise CLIError('Extension repo path is empty. Please try `azdev extension repo add` to add an extension repo')
319
- all_extensions = find_files(ext_paths, 'setup.py')
332
+ all_extensions = find_package_configs_recursive(ext_paths)
320
333
 
321
334
  paths_to_build = []
322
335
  for path in all_extensions:
@@ -329,15 +342,10 @@ def build_extensions(extensions, dist_dir='dist'):
329
342
  if extensions:
330
343
  raise CLIError('extension(s) not found: {}'.format(' '.join(extensions)))
331
344
 
332
- original_cwd = os.getcwd()
333
- dist_dir = os.path.join(original_cwd, dist_dir)
345
+ dist_dir = os.path.join(os.getcwd(), dist_dir)
334
346
  for path in paths_to_build:
335
- os.chdir(path)
336
- command = 'setup.py bdist_wheel -b bdist -d {}'.format(dist_dir)
337
- result = py_cmd(command, "Building extension '{}'...".format(path), is_module=False)
338
- if result.error:
339
- raise result.error # pylint: disable=raising-bad-type
340
- os.chdir(original_cwd)
347
+ display("Building extension '{}'...".format(path))
348
+ build_package_wheel(path, dist_dir)
341
349
 
342
350
 
343
351
  def publish_extensions(extensions, storage_account, storage_account_key, storage_container,
@@ -82,6 +82,7 @@ def read_pkginfo(wheel_path: Path) -> Dict[str, Any]:
82
82
  "description": whl.description,
83
83
  "description_content_type": whl.description_content_type,
84
84
  "license": whl.license,
85
+ "license_expression": getattr(whl, "license_expression", None),
85
86
  "classifiers": list(whl.classifiers or []),
86
87
  "requires_dist": list(whl.requires_dist or []),
87
88
  "requires_python": whl.requires_python,
@@ -149,13 +150,27 @@ def _coerce_run_requires(requires_dist: List[str]) -> List[Dict[str, Any]]:
149
150
 
150
151
 
151
152
  def _coerce_project_urls(project_urls: List[str], home_page: Optional[str]) -> Dict[str, str]:
153
+ """Build the `project_urls` block, normalizing the home page label.
154
+
155
+ A `setup.py` package declares `url=`, which lands in the `Home-page` METADATA
156
+ field and has always been stored in `index.json` under the `Home` label.
157
+ PEP 621 has no `url` equivalent: a pyproject package declares
158
+ `[project.urls] Homepage`, which lands in `Project-URL` instead. Both are
159
+ mapped to `Home` so an extension's index entry keeps its shape when it
160
+ migrates.
161
+ """
152
162
  out: Dict[str, str] = {}
153
163
  if home_page:
154
164
  out["Home"] = home_page
155
165
  for entry in project_urls or []:
156
- if "," in entry:
157
- label, url = entry.split(",", 1)
158
- out[label.strip()] = url.strip()
166
+ if "," not in entry:
167
+ continue
168
+ label, url = entry.split(",", 1)
169
+ label, url = label.strip(), url.strip()
170
+ if label.lower() in ("home", "homepage", "home-page"):
171
+ out.setdefault("Home", url)
172
+ continue
173
+ out[label] = url
159
174
  return out
160
175
 
161
176
 
@@ -180,7 +195,9 @@ def merge_to_index_metadata(pkg: Dict[str, Any], azext: Dict[str, Any]) -> Dict[
180
195
  metadata["name"] = pkg.get("name")
181
196
  metadata["version"] = pkg.get("version")
182
197
  metadata["summary"] = pkg.get("summary")
183
- metadata["license"] = pkg.get("license")
198
+ # PEP 639 wheels carry the license as `License-Expression` rather than
199
+ # `License`, so accept either and keep a single `license` key in the index.
200
+ metadata["license"] = pkg.get("license") or pkg.get("license_expression")
184
201
  metadata["metadata_version"] = pkg.get("metadata_version")
185
202
  metadata["classifiers"] = pkg.get("classifiers") or []
186
203
  metadata["extras"] = []
@@ -17,10 +17,7 @@ _LATEST_INDEX_SCRIPT = os.path.join('scripts', 'generate_latest_indices.py')
17
17
 
18
18
 
19
19
  def _resolve_cli_repo_path(cli_path):
20
- if cli_path:
21
- resolved = os.path.abspath(os.path.expanduser(cli_path))
22
- else:
23
- resolved = get_cli_repo_path()
20
+ resolved = os.path.abspath(os.path.expanduser(cli_path)) if cli_path else get_cli_repo_path()
24
21
 
25
22
  if not resolved or resolved == '_NONE_':
26
23
  raise CLIError('Azure CLI repo path is not configured. Specify `--cli` or run `azdev setup`.')