azure-cli-extensions: update docs on adding extension

Closes https://github.com/NixOS/nixpkgs/issues/342214

Signed-off-by: Paul Meyer <katexochen0@gmail.com>

+69 -16
+14 -12
pkgs/by-name/az/azure-cli/README.md
··· 33 and the name of the extension you want to package as `extension`: 34 35 ```sh 36 - ./query-extension-index.sh --cli-version=2.61.0 --extension=azure-devops --download 37 ``` 38 39 The 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 ··· 71 72 * 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
··· 33 and the name of the extension you want to package as `extension`: 34 35 ```sh 36 + nix run .#azure-cli.extension-tool -- \ 37 + --cli-version=2.61.0 \ 38 + --extension=azure-devops \ 39 + --init 40 ``` 41 42 The 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)" 54 ] 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 ··· 73 74 * 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) 175 176 177 - 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"]: 191 return None 192 193 return _transform_dict_to_obj(latest) ··· 335 action=argparse.BooleanOptionalAction, 336 help="whether to commit changes to git", 337 ) 338 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"] 350 351 if args.extension: 352 logger.info(f"updating extension: {args.extension}") 353 354 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: ··· 402 403 extensions_remote_filtered = set() 404 for _ext_name, extension in extensions_remote.items(): 405 - extension = processExtension(extension, cli_version, args.extension) 406 if extension: 407 extensions_remote_filtered.add(extension) 408
··· 174 return max(versions, key=lambda e: parse(e["metadata"]["version"]), default=None) 175 176 177 + 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 204 205 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"] 367 368 + # 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}") 402 403 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: ··· 451 452 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