diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 23c9d3c15..6603c0309 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -40,7 +40,7 @@ jobs: python-version: ${{ matrix.py }} - uses: actions/checkout@v4 - name: Install tox-gh - run: python -m pip install tox-gh + run: python -m pip install tox-gh tox==4.12.1 - name: Setup test suite run: tox r -vv --notest - name: Run test suite @@ -53,7 +53,7 @@ jobs: - uses: actions/checkout@v4 - name: Run code coverage run: | - python -m pip install tox + python -m pip install tox==4.12.1 tox -e coverage coverage=$(jq .totals.percent_covered coverage.json | cut -c1-4) echo "Code coverage: $coverage%" >> $GITHUB_STEP_SUMMARY diff --git a/HISTORY.rst b/HISTORY.rst index a4da4d68a..ad77715da 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -7,6 +7,9 @@ Release History 0.24.0 +++++++++++++++ +**General updates** + +* Updated the minimum core CLI version to 2.46.0 **IoT Product** diff --git a/README.md b/README.md index f44eaf2d2..fc5409cbe 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ Please refer to the official `az iot` reference on [Microsoft Docs](https://docs ## Installation 1. Install the [Azure CLI](https://docs.microsoft.com/en-us/cli/azure/install-azure-cli) - - You must have at least `v2.32.0` for the latest versions of `azure-iot`, which you can verify with `az --version` + - You must have at least `v2.46.0` for the latest versions of `azure-iot`, which you can verify with `az --version` 1. Add, Update or Remove the IoT extension with the following commands: - Add: `az extension add --name azure-iot` - Update: `az extension update --name azure-iot` diff --git a/azext_iot/_help.py b/azext_iot/_help.py index d768a5289..28aaaa239 100644 --- a/azext_iot/_help.py +++ b/azext_iot/_help.py @@ -1114,7 +1114,7 @@ text: > az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --attestation-type tpm --allocation-policy hashed - --endorsement-key 14963E8F3BA5B3984110B3C1CA8E8B89 --iot-hubs "{iot_hub_host_name1} {iot_hub_host_name2}" + --endorsement-key 14963E8F3BA5B3984110B3C1CA8E8B89 --iot-hubs {iot_hub_host_name1} {iot_hub_host_name2} - name: Create an enrollment 'MyEnrollment' with custom allocation policy, text: > az iot dps enrollment create -g {resource_group_name} --dps-name {dps_name} @@ -1161,7 +1161,7 @@ text: > az iot dps enrollment update -g {resource_group_name} --dps-name {dps_name} --enrollment-id {enrollment_id} --allocation-policy geolatency - --etag AAAAAAAAAAA= --iot-hubs "{iot_hub_host_name1} {iot_hub_host_name2} {iot_hub_host_name3}" + --etag AAAAAAAAAAA= --iot-hubs {iot_hub_host_name1} {iot_hub_host_name2} {iot_hub_host_name3} - name: Update enrollment '{enrollment_id}' in the Azure IoT Device Provisioning Service '{dps_name}' in the resource group '{resource_group_name}' with initial twin properties '{"location":{"region":"USA"}}', initial twin tags '{"version":"2"}', diff --git a/azext_iot/_params.py b/azext_iot/_params.py index 16f50509d..54bf532db 100644 --- a/azext_iot/_params.py +++ b/azext_iot/_params.py @@ -1006,6 +1006,8 @@ def load_arguments(self, _): options_list=["--iot-hubs", "--ih"], help="Host name of target IoT Hub associated with the allocation policy. Use space-separated " "list for multiple IoT Hubs.", + nargs="+", + action="extend", arg_group="Allocation Policy" ) context.argument( diff --git a/azext_iot/azext_metadata.json b/azext_iot/azext_metadata.json index fb043f28f..950a93d6e 100644 --- a/azext_iot/azext_metadata.json +++ b/azext_iot/azext_metadata.json @@ -1,3 +1,3 @@ { - "azext.minCliCoreVersion": "2.37.0" + "azext.minCliCoreVersion": "2.46.0" } diff --git a/azext_iot/deviceupdate/commands_update.py b/azext_iot/deviceupdate/commands_update.py index e7794c19a..a0addc949 100644 --- a/azext_iot/deviceupdate/commands_update.py +++ b/azext_iot/deviceupdate/commands_update.py @@ -236,8 +236,8 @@ def manifest_init_v5( ): from datetime import datetime from pathlib import PurePath - from azure.cli.core.azclierror import ArgumentUsageError, InvalidArgumentValueError - from azext_iot.deviceupdate.common import FP_HANDLERS, FP_HANDLERS_REQUIRE_CRITERIA + from azure.cli.core.azclierror import ArgumentUsageError + from azext_iot.deviceupdate.common import FP_HANDLERS_REQUIRE_CRITERIA from azext_iot.deviceupdate.providers.utility import parse_manifest_json def _sanitize_safe_params(safe_params: list, keep: list) -> list: @@ -323,10 +323,6 @@ def _associate_related(sanitized_params: list, key: str) -> dict: "handler": assembled_step["handler"], } - if step["handler"].lower().startswith("microsoft") and step["handler"] not in FP_HANDLERS: - if not no_validation: - raise InvalidArgumentValueError(f"Valid Microsoft handlers: {', '.join(FP_HANDLERS)}") - step["files"] = ( list(set([f.strip() for f in assembled_step["files"].split(",")])) if "files" in assembled_step else [] ) diff --git a/azext_iot/operations/dps.py b/azext_iot/operations/dps.py index 985c70f0a..845d6ce41 100644 --- a/azext_iot/operations/dps.py +++ b/azext_iot/operations/dps.py @@ -181,7 +181,7 @@ def iot_dps_device_enrollment_create( ) reprovision = _get_reprovision_policy(reprovision_policy) initial_twin = _get_initial_twin(initial_twin_tags, initial_twin_properties) - iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs + iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs _validate_allocation_policy_for_enrollment( allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version ) @@ -303,18 +303,27 @@ def iot_dps_device_enrollment_update( enrollment_record.initial_twin = _get_updated_inital_twin( enrollment_record, initial_twin_tags, initial_twin_properties ) - iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs + iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs _validate_allocation_policy_for_enrollment( - allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version + allocation_policy, + iot_hub_host_name, + iot_hub_list, + webhook_url, + api_version, + current_enrollment=enrollment_record ) - if allocation_policy: - enrollment_record.allocation_policy = allocation_policy + if iot_hub_list: enrollment_record.iot_hubs = iot_hub_list enrollment_record.iot_hub_host_name = None - if allocation_policy == AllocationType.custom.value: - enrollment_record.custom_allocation_definition = CustomAllocationDefinition( - webhook_url=webhook_url, api_version=api_version - ) + if allocation_policy: + enrollment_record.allocation_policy = allocation_policy + if enrollment_record.allocation_policy == AllocationType.custom.value and any([ + webhook_url, api_version + ]): + enrollment_record.custom_allocation_definition = CustomAllocationDefinition( + webhook_url=webhook_url or enrollment_record.custom_allocation_definition.webhook_url, + api_version=api_version or enrollment_record.custom_allocation_definition.api_version + ) if edge_enabled is not None: enrollment_record.capabilities = DeviceCapabilities(iot_edge=edge_enabled) if device_information: @@ -481,7 +490,7 @@ def iot_dps_device_enrollment_group_create( ) reprovision = _get_reprovision_policy(reprovision_policy) initial_twin = _get_initial_twin(initial_twin_tags, initial_twin_properties) - iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs + iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs _validate_allocation_policy_for_enrollment( allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version ) @@ -621,18 +630,27 @@ def iot_dps_device_enrollment_group_update( enrollment_record.initial_twin = _get_updated_inital_twin( enrollment_record, initial_twin_tags, initial_twin_properties ) - iot_hub_list = iot_hubs.split() if iot_hubs else iot_hubs + iot_hub_list = iot_hubs.split() if isinstance(iot_hubs, str) else iot_hubs _validate_allocation_policy_for_enrollment( - allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version + allocation_policy, + iot_hub_host_name, + iot_hub_list, + webhook_url, + api_version, + current_enrollment=enrollment_record ) - if allocation_policy: - enrollment_record.allocation_policy = allocation_policy + if iot_hub_list: enrollment_record.iot_hubs = iot_hub_list enrollment_record.iot_hub_host_name = None - if allocation_policy == AllocationType.custom.value: - enrollment_record.custom_allocation_definition = CustomAllocationDefinition( - webhook_url=webhook_url, api_version=api_version - ) + if allocation_policy: + enrollment_record.allocation_policy = allocation_policy + if enrollment_record.allocation_policy == AllocationType.custom.value and any([ + webhook_url, api_version + ]): + enrollment_record.custom_allocation_definition = CustomAllocationDefinition( + webhook_url=webhook_url or enrollment_record.custom_allocation_definition.webhook_url, + api_version=api_version or enrollment_record.custom_allocation_definition.api_version + ) if edge_enabled is not None: enrollment_record.capabilities = DeviceCapabilities(iot_edge=edge_enabled) return sdk.enrollment_group.create_or_update( @@ -1137,8 +1155,16 @@ def _validate_arguments_for_attestation_mechanism( def _validate_allocation_policy_for_enrollment( - allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version + allocation_policy, iot_hub_host_name, iot_hub_list, webhook_url, api_version, current_enrollment=None ): + # get the enrollment values if not provided but present + if current_enrollment: + iot_hub_list = iot_hub_list or current_enrollment.iot_hubs + allocation_policy = allocation_policy or current_enrollment.allocation_policy + if current_enrollment.allocation_policy == AllocationType.custom.value: + webhook_url = webhook_url or current_enrollment.custom_allocation_definition.webhook_url + api_version = api_version or current_enrollment.custom_allocation_definition.api_version + if allocation_policy: if iot_hub_host_name is not None: raise MutuallyExclusiveArgumentError( @@ -1149,6 +1175,7 @@ def _validate_allocation_policy_for_enrollment( allocation_policy == allocation.value for allocation in AllocationType ): raise RequiredArgumentMissingError("Please provide valid allocation policy.") + if allocation_policy == AllocationType.static.value: if iot_hub_list is None: raise RequiredArgumentMissingError("Please provide a hub to be assigned with device.") @@ -1160,6 +1187,5 @@ def _validate_allocation_policy_for_enrollment( "Please provide both the Azure function webhook url and provisioning" " service api-version when the allocation-policy is defined as Custom." ) - else: - if iot_hub_list: - raise RequiredArgumentMissingError("Please provide allocation policy.") + elif iot_hub_list and not current_enrollment: + raise RequiredArgumentMissingError("Please provide allocation policy.") diff --git a/azext_iot/tests/deviceupdate/test_adu_manifest_int.py b/azext_iot/tests/deviceupdate/test_adu_manifest_int.py index 54dd9877f..d52667a4d 100644 --- a/azext_iot/tests/deviceupdate/test_adu_manifest_int.py +++ b/azext_iot/tests/deviceupdate/test_adu_manifest_int.py @@ -424,6 +424,46 @@ "manifestVersion": "5.0", }, ), + ( + "--update-provider digimaun0 --update-name customhandler --update-version 0.1 " + "--compat manufacturer=Contoso model=Vacuum " + "--compat ring=0 tier=test " + "--step handler=microsoft/customhandler:2 " + f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" " + f"--related-file path=\"{get_context_path(__file__, 'manifests', 'surface15', 'parent.importmanifest.json')}\" ", + { + "updateId": {"provider": "digimaun0", "name": "customhandler", "version": "0.1"}, + "compatibility": [ + {"manufacturer": "Contoso", "model": "Vacuum"}, + {"ring": "0", "tier": "test"}, + ], + "instructions": { + # no first party handler validation + "steps": [ + { + "handler": "microsoft/customhandler:2", + "files": ["libcurl4-doc-apt-manifest.json"], + "type": "inline", + }, + ] + }, + "files": [ + { + "filename": "libcurl4-doc-apt-manifest.json", + "sizeInBytes": 163, + "hashes": {"sha256": "iFWTIaxp33tf5BR1w0fMmnnHpjsUjLRQ9eZFjw74LbU="}, + "relatedFiles": [ + { + "filename": "parent.importmanifest.json", + "sizeInBytes": 1390, + "hashes": {"sha256": "hos1UvCk66WmtL/SPNUmub+k302BM4gtWYtAF7tOCb4="}, + } + ], + } + ], + "manifestVersion": "5.0", + }, + ), ], ) def test_adu_manifest_init_v5(options, expected): @@ -514,22 +554,6 @@ def test_adu_manifest_init_v5_invalid_path_required(options): "downloadHandler=abc", True, ), - ( - # If content handler starts with microsoft (case-insensitive) enforce valid value. - "--update-provider digimaun --update-name invalid --update-version 1.0 " - "--compat deviceManufacturer=Contoso deviceModel=Vacuum " - "--step handler=microsoft/fake:1 " - f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" ", - False, - ), - ( - # Same as prior test case but ensure escape hatch with --no-validation - "--update-provider digimaun --update-name invalid --update-version 1.0 " - "--compat deviceManufacturer=Contoso deviceModel=Vacuum " - "--step handler=microsoft/fake:1 " - f"--file path=\"{get_context_path(__file__, 'manifests', 'libcurl4-doc-apt-manifest.json')}\" ", - False, - ), ], ) def test_adu_manifest_init_v5_validate_errors(options, no_validation): diff --git a/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_int.py b/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_int.py index 77eb699a9..6db46d72b 100644 --- a/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_int.py +++ b/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_int.py @@ -298,7 +298,7 @@ def test_dps_enrollment_symmetrickey_lifecycle(provisioned_iot_dps_module): assert update_enrollment["customAllocationDefinition"]["webhookUrl"] == WEBHOOK_URL assert update_enrollment["customAllocationDefinition"]["apiVersion"] == API_VERSION assert update_enrollment["deviceId"] == device_id - assert update_enrollment["iotHubs"] is None + assert update_enrollment["iotHubs"] == [hub_hostname] assert update_enrollment["initialTwin"]["tags"] assert update_enrollment["initialTwin"]["properties"]["desired"] assert update_enrollment["optionalDeviceInformation"] == generic_dict diff --git a/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_unit.py b/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_unit.py index 291f36aba..d111ca12d 100644 --- a/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_unit.py +++ b/azext_iot/tests/dps/enrollment/test_iot_dps_enrollment_unit.py @@ -123,6 +123,12 @@ def serviceclient_generic_error(self, mocked_response, fixture_gdcs, fixture_dps reprovision_policy='never', allocation_policy='hashed', iot_hubs='hub1 hub2')), + (generate_enrollment_create_req(attestation_type='symmetricKey', + primary_key='primarykey', + secondary_key='secondarykey', + reprovision_policy='never', + allocation_policy='hashed', + iot_hubs=['hub1', 'hub2'])), (generate_enrollment_create_req(attestation_type='symmetricKey', primary_key='primarykey', secondary_key='secondarykey', @@ -227,7 +233,9 @@ def test_enrollment_create(self, serviceclient, fixture_cmd, req): assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url'] assert body['customAllocationDefinition']['apiVersion'] == req['api_version'] if req['iot_hubs']: - assert body['iotHubs'] == req['iot_hubs'].split() + assert body['iotHubs'] == ( + req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs'] + ) if req['edge_enabled']: assert body['capabilities']['iotEdge'] @@ -376,6 +384,7 @@ def serviceclient(self, mocked_response, fixture_gdcs, fixture_dps_sas, patch_ce (generate_enrollment_update_req(reprovision_policy='never')), (generate_enrollment_update_req(allocation_policy='static', iot_hubs='hub1')), (generate_enrollment_update_req(allocation_policy='hashed', iot_hubs='hub1 hub2')), + (generate_enrollment_update_req(allocation_policy='hashed', iot_hubs=['hub1', 'hub2'])), (generate_enrollment_update_req(allocation_policy='geoLatency')), (generate_enrollment_update_req(allocation_policy='custom', webhook_url="https://www.test.test", @@ -383,7 +392,8 @@ def serviceclient(self, mocked_response, fixture_gdcs, fixture_dps_sas, patch_ce (generate_enrollment_update_req(edge_enabled=True)), (generate_enrollment_update_req(edge_enabled=False)) ]) - def test_enrollment_update(self, serviceclient, fixture_cmd, req): + def test_enrollment_update(self, mocker, serviceclient, fixture_cmd, req): + mocker.patch("azext_iot.operations.dps._validate_allocation_policy_for_enrollment") subject.iot_dps_device_enrollment_update( cmd=fixture_cmd, enrollment_id=req['enrollment_id'], @@ -457,7 +467,9 @@ def test_enrollment_update(self, serviceclient, fixture_cmd, req): assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url'] assert body['customAllocationDefinition']['apiVersion'] == req['api_version'] if req['iot_hubs']: - assert body['iotHubs'] == req['iot_hubs'].split() + assert body['iotHubs'] == ( + req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs'] + ) if req['edge_enabled'] is not None: assert body['capabilities']['iotEdge'] == req['edge_enabled'] diff --git a/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_int.py b/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_int.py index 6f92e8029..949889ab7 100644 --- a/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_int.py +++ b/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_int.py @@ -248,7 +248,7 @@ def test_dps_enrollment_group_symmetrickey_lifecycle(provisioned_iot_dps_module) assert enrollment_update["customAllocationDefinition"]["webhookUrl"] == WEBHOOK_URL assert enrollment_update["customAllocationDefinition"]["apiVersion"] == API_VERSION assert enrollment_update["enrollmentGroupId"] == enrollment_id - assert enrollment_update["iotHubs"] is None + assert enrollment_update["iotHubs"] == [hub_hostname] assert enrollment_update["initialTwin"]["tags"] assert enrollment_update["initialTwin"]["properties"]["desired"] assert enrollment_update["provisioningStatus"] == EntityStatusType.disabled.value diff --git a/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_unit.py b/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_unit.py index 4fc3ad4d9..0377ca370 100644 --- a/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_unit.py +++ b/azext_iot/tests/dps/enrollment_group/test_iot_dps_enrollment_group_unit.py @@ -96,6 +96,9 @@ def serviceclient(self, mocked_response, fixture_gdcs, fixture_dps_sas, patch_ce (generate_enrollment_group_create_req(certificate_path='myCert', allocation_policy='hashed', iot_hubs='hub1 hub2')), + (generate_enrollment_group_create_req(certificate_path='myCert', + allocation_policy='hashed', + iot_hubs=['hub1', 'hub2'])), (generate_enrollment_group_create_req(certificate_path='myCert', allocation_policy='geoLatency')), (generate_enrollment_group_create_req(certificate_path='myCert', @@ -190,7 +193,9 @@ def test_enrollment_group_create(self, serviceclient, fixture_cmd, req): assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url'] assert body['customAllocationDefinition']['apiVersion'] == req['api_version'] if req['iot_hubs']: - assert body['iotHubs'] == req['iot_hubs'].split() + assert body['iotHubs'] == (( + req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs']) + ) if req['edge_enabled']: assert body['capabilities']['iotEdge'] @@ -362,12 +367,14 @@ def serviceclient(self, mocked_response, fixture_gdcs, fixture_dps_sas, patch_ce webhook_url="https://www.test.test", api_version="2019-03-31")), (generate_enrollment_group_update_req(allocation_policy='hashed', iot_hubs='hub1 hub2')), + (generate_enrollment_group_update_req(allocation_policy='hashed', iot_hubs=['hub1', 'hub2'])), (generate_enrollment_group_update_req(allocation_policy='geoLatency')), (generate_enrollment_group_update_req(iot_hub_host_name='hub1')), (generate_enrollment_group_update_req(edge_enabled=True)), (generate_enrollment_group_update_req(edge_enabled=False)) ]) - def test_enrollment_group_update(self, serviceclient, fixture_cmd, req): + def test_enrollment_group_update(self, mocker, serviceclient, fixture_cmd, req): + mocker.patch("azext_iot.operations.dps._validate_allocation_policy_for_enrollment") subject.iot_dps_device_enrollment_group_update( cmd=fixture_cmd, enrollment_id=req['enrollment_id'], @@ -445,7 +452,9 @@ def test_enrollment_group_update(self, serviceclient, fixture_cmd, req): assert body['customAllocationDefinition']['webhookUrl'] == req['webhook_url'] assert body['customAllocationDefinition']['apiVersion'] == req['api_version'] if req['iot_hubs']: - assert body['iotHubs'] == req['iot_hubs'].split() + assert body['iotHubs'] == (( + req['iot_hubs'].split() if isinstance(req['iot_hubs'], str) else req['iot_hubs']) + ) if req['edge_enabled'] is not None: assert body['capabilities']['iotEdge'] == req['edge_enabled'] diff --git a/dev_requirements b/dev_requirements index 845c1a7c2..2c822fdcd 100644 --- a/dev_requirements +++ b/dev_requirements @@ -1,4 +1,4 @@ -pytest +pytest==8.1.1 pytest-mock==3.12.0 pytest-cov pytest-env @@ -11,4 +11,3 @@ pylint<=2.13.8 flake8 pytest-xdist pytest-rerunfailures -tox diff --git a/docs/tox-testing.md b/docs/tox-testing.md index 17699a435..8c140d93d 100644 --- a/docs/tox-testing.md +++ b/docs/tox-testing.md @@ -10,7 +10,7 @@ Currently, our testing matrix is broken up into the following groups: - 3.10 - 3.11 - Azure CLI Core versions to test extension against: - - `azmin` installs the minimum supported CLI version (currently `2.32.0`) + - `azmin` installs the minimum supported CLI version (currently `2.46.0`) - `azcur` installs the latest released CLI version from PyPi - `azdev` installs the CLI from your local CLI instance (located at `../azure-cli`) - Types of tests to run: diff --git a/tox.ini b/tox.ini index 4cc2e357d..7a7cabc8e 100644 --- a/tox.ini +++ b/tox.ini @@ -56,7 +56,7 @@ deps = # base deps {[base]deps} # azure-cli deps - azmin: azure-cli==2.32.0 + azmin: azure-cli==2.46.0 azcur: azure-cli azdev: ../azure-cli/src/azure-cli azdev: ../azure-cli/src/azure-cli-core @@ -70,8 +70,8 @@ commands = az -v # run tests # You can pass additional positional args to pytest using `-- {args}` - unit: pytest -k _unit ./azext_iot/tests {posargs} - int: pytest -k _int ./azext_iot/tests {posargs} + unit: pytest -k _unit.py ./azext_iot/tests {posargs} + int: pytest -k _int.py ./azext_iot/tests {posargs} # tox-gh matrix (github action -> tox python environment) [gh]