···33and the name of the extension you want to package as `extension`:
3435```sh
36-./query-extension-index.sh --cli-version=2.61.0 --extension=azure-devops --download
00037```
3839The output should look something like this:
···41```json
42{
43 "pname": "azure-devops",
44- "description": "Tools for managing Azure DevOps.",
45- "version": "1.0.1",
46- "url": "https://github.com/Azure/azure-devops-cli-extension/releases/download/20240514.1/azure_devops-1.0.1-py2.py3-none-any.whl",
47- "sha256": "f300d0288f017148514ebe6f5912aef10c7a6f29bdc0c916b922edf1d75bc7db",
48 "license": "MIT",
49- "requires": [
50- "distro (==1.3.0)",
51- "distro==1.3.0"
52 ]
53}
54```
···58```nix
59 azure-devops = mkAzExtension {
60 pname = "azure-devops";
61- version = "1.0.0";
62- url = "https://github.com/Azure/azure-devops-cli-extension/releases/download/20240206.1/azure_devops-${version}-py2.py3-none-any.whl";
63- sha256 = "658a2854d8c80f874f9382d421fa45abf6a38d00334737dda006f8dec64cf70a";
64 description = "Tools for managing Azure DevOps";
65 propagatedBuildInputs = with python3Packages; [
66 distro
···7172* The attribute name should be the same as `pname`.
73* Replace the version in `url` with `${version}`.
74-* The json output `requires` must be transformed into `propagetedBuildInputs`.
75* If `license` is `"MIT"`, it can be left out in the nix expression, as the builder defaults to that license.
76* Add yourself as maintainer in `meta.maintainers`.
77
···33and the name of the extension you want to package as `extension`:
3435```sh
36+nix run .#azure-cli.extension-tool -- \
37+ --cli-version=2.61.0 \
38+ --extension=azure-devops \
39+ --init
40```
4142The output should look something like this:
···44```json
45{
46 "pname": "azure-devops",
47+ "version": "1.0.2",
48+ "url": "https://github.com/Azure/azure-devops-cli-extension/releases/download/20250624.2/azure_devops-1.0.2-py2.py3-none-any.whl",
49+ "hash": "sha256-4rDeAqOnRRKMP26MJxG4u9vBuos6/SQIoVgfNbBpulk=",
50+ "description": "Tools for managing Azure DevOps",
51 "license": "MIT",
52+ "requirements": [
53+ "distro (>=1.6.0)"
054 ]
55}
56```
···60```nix
61 azure-devops = mkAzExtension {
62 pname = "azure-devops";
63+ version = "1.0.2";
64+ url = "https://github.com/Azure/azure-devops-cli-extension/releases/download/20250624.2/azure_devops-${version}-py2.py3-none-any.whl";
65+ hash = "sha256-4rDeAqOnRRKMP26MJxG4u9vBuos6/SQIoVgfNbBpulk=";
66 description = "Tools for managing Azure DevOps";
67 propagatedBuildInputs = with python3Packages; [
68 distro
···7374* The attribute name should be the same as `pname`.
75* Replace the version in `url` with `${version}`.
76+* The json output `requirements` must be transformed into package `requirements`.
77* If `license` is `"MIT"`, it can be left out in the nix expression, as the builder defaults to that license.
78* Add yourself as maintainer in `meta.maintainers`.
79
+55-4
pkgs/by-name/az/azure-cli/extensions-tool.py
···174 return max(versions, key=lambda e: parse(e["metadata"]["version"]), default=None)
175176177-def processExtension(
178 extVersions: dict,
179 cli_version: Version,
180 ext_name: Optional[str] = None,
181 requirements: bool = False,
182-) -> Optional[Ext]:
183 versions = filter(_filter_invalid, extVersions)
184 versions = filter(lambda v: _filter_compatible(v, cli_version), versions)
185 latest = _get_latest_version(versions)
···188 if ext_name and latest["metadata"]["name"] != ext_name:
189 return None
190 if not requirements and "run_requires" in latest["metadata"]:
000000000000191 return None
192193 return _transform_dict_to_obj(latest)
···335 action=argparse.BooleanOptionalAction,
336 help="whether to commit changes to git",
337 )
00000338 args = parser.parse_args()
339 cli_version = parse(args.cli_version)
340···348 assert index["formatVersion"] == "1" # only support formatVersion 1
349 extensions_remote = index["extensions"]
35000000000000000000000000000000000351 if args.extension:
352 logger.info(f"updating extension: {args.extension}")
353354 ext = Optional[Ext]
355 for _ext_name, extension in extensions_remote.items():
356- extension = processExtension(
357 extension, cli_version, args.extension, requirements=True
358 )
359 if extension:
···402403 extensions_remote_filtered = set()
404 for _ext_name, extension in extensions_remote.items():
405- extension = processExtension(extension, cli_version, args.extension)
00406 if extension:
407 extensions_remote_filtered.add(extension)
408
···174 return max(versions, key=lambda e: parse(e["metadata"]["version"]), default=None)
175176177+def find_extension_version(
178 extVersions: dict,
179 cli_version: Version,
180 ext_name: Optional[str] = None,
181 requirements: bool = False,
182+) -> Optional[Dict[str, Any]]:
183 versions = filter(_filter_invalid, extVersions)
184 versions = filter(lambda v: _filter_compatible(v, cli_version), versions)
185 latest = _get_latest_version(versions)
···188 if ext_name and latest["metadata"]["name"] != ext_name:
189 return None
190 if not requirements and "run_requires" in latest["metadata"]:
191+ return None
192+ return latest
193+194+195+def find_and_transform_extension_version(
196+ extVersions: dict,
197+ cli_version: Version,
198+ ext_name: Optional[str] = None,
199+ requirements: bool = False,
200+) -> Optional[Ext]:
201+ latest = find_extension_version(extVersions, cli_version, ext_name, requirements)
202+ if not latest:
203 return None
204205 return _transform_dict_to_obj(latest)
···347 action=argparse.BooleanOptionalAction,
348 help="whether to commit changes to git",
349 )
350+ parser.add_argument(
351+ "--init",
352+ action=argparse.BooleanOptionalAction,
353+ help="whether you want to init a new extension",
354+ )
355 args = parser.parse_args()
356 cli_version = parse(args.cli_version)
357···365 assert index["formatVersion"] == "1" # only support formatVersion 1
366 extensions_remote = index["extensions"]
367368+ # init just prints the json of the extension version that matches the cli version.
369+ if args.init:
370+ if not args.extension:
371+ logger.error("extension name is required for --init")
372+ exit(1)
373+374+ for ext_name, ext_versions in extensions_remote.items():
375+ if ext_name != args.extension:
376+ continue
377+ ext = find_extension_version(
378+ ext_versions,
379+ cli_version,
380+ args.extension,
381+ requirements=True,
382+ )
383+ break
384+ if not ext:
385+ logger.error(f"Extension {args.extension} not found in index")
386+ exit(1)
387+388+ ext_translated = {
389+ "pname": ext["metadata"]["name"],
390+ "version": ext["metadata"]["version"],
391+ "url": ext["downloadUrl"],
392+ "hash": _convert_hash_digest_from_hex_to_b64_sri(ext["sha256Digest"]),
393+ "description": ext["metadata"]["summary"].rstrip("."),
394+ "license": ext["metadata"]["license"],
395+ "requirements": ext["metadata"]["run_requires"][0]["requires"],
396+ }
397+ print(json.dumps(ext_translated, indent=2))
398+ return
399+400 if args.extension:
401 logger.info(f"updating extension: {args.extension}")
402403 ext = Optional[Ext]
404 for _ext_name, extension in extensions_remote.items():
405+ extension = find_and_transform_extension_version(
406 extension, cli_version, args.extension, requirements=True
407 )
408 if extension:
···451452 extensions_remote_filtered = set()
453 for _ext_name, extension in extensions_remote.items():
454+ extension = find_and_transform_extension_version(
455+ extension, cli_version, args.extension
456+ )
457 if extension:
458 extensions_remote_filtered.add(extension)
459