Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
95 changes: 95 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: CI

"on":
pull_request:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.number || github.run_id }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
lint:
name: Ruff
runs-on: ubuntu-24.04
steps:
- name: Check out repository
# v7.0.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Set up Python
# v7.0.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: 3.x
- name: Set up uv
# v10.0.1
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
with:
enable-cache: true
cache-suffix: lint-upgrade
- name: Sync lint environment
run: uv sync --upgrade --no-dev --group lint
- name: Run Ruff
run: uv run --no-sync ruff check .

test:
name: Python ${{ matrix.python-version }}
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.11"
- "3.12"
- "3.13"
- "3.14"
steps:
- name: Check out repository
# v7.0.1
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
# v7.0.0
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97
with:
python-version: ${{ matrix.python-version }}
- name: Set up uv
# v10.0.1
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d
with:
enable-cache: true
cache-suffix: test-${{ matrix.python-version }}-upgrade
- name: Sync test environment
run: >-
uv sync --upgrade --python "${{ matrix.python-version }}"
--no-dev --group test
- name: Run unit tests
run: >-
uv run --no-sync pytest tests
--junitxml=junit/azure-iot-device-test-results.xml
--cov=azure
--cov-report=xml:coverage.xml
--cov-report=html:coverage
--cov-append
- name: Upload test results and coverage
if: ${{ always() }}
# v7.0.1
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a
with:
name: test-results-python-${{ matrix.python-version }}
path: |
junit/
coverage.xml
coverage/
if-no-files-found: error
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<h1> azure-iot-device </h1>
</div>

![Build Status](https://azure-iot-sdks.visualstudio.com/azure-iot-sdks/_apis/build/status/Azure.azure-iot-sdk-python)
[![Build Status](https://github.com/Azure/azure-iot-sdk-python/actions/workflows/ci.yml/badge.svg)](https://github.com/Azure/azure-iot-sdk-python/actions/workflows/ci.yml)

The Azure IoT Device SDK for Python enables Python developers to easily create IoT device solutions that seamlessly connect to the Azure IoT Hub ecosystem.

Expand Down
79 changes: 16 additions & 63 deletions vsts/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ resources:

variables:
# CodeQL is auto-injected into every job by the 1ES policy decorator. Off by
# default so the python test matrix (5 parallel jobs) does not each build and
# upload a competing database -- S360 records arg_max(SnapshotDate) per
# (repo, language). The dedicated 'CodeQL' job below opts back in.
# default so other jobs do not build and upload competing databases -- S360
# records arg_max(SnapshotDate) per (repo, language). The dedicated 'CodeQL'
# job below opts back in.
# See https://aka.ms/codeql3000 and https://aka.ms/sdlfaqcodeql.
Codeql.Enabled: false

Expand Down Expand Up @@ -39,77 +39,18 @@ jobs:
condition: always()

- job: 'Static_Analysis'
displayName: 'Component Governance'
pool:
vmImage: 'ubuntu-24.04'

steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.x'
architecture: 'x64'
- task: ComponentGovernanceComponentDetection@0
inputs:
scanType: 'Register'
verbosity: 'Verbose'
alertWarningLevel: 'High'

- script: 'python -m pip install uv'
displayName: 'Install uv'

- script: 'uv sync --upgrade --no-dev --group lint'
displayName: 'Sync lint environment'

- script: 'uv run --no-sync ruff check .'
displayName: 'Ruff check'

- job: 'Test'
pool:
vmImage: 'ubuntu-24.04'
strategy:
matrix:
Python310:
python.version: '3.10'
Python311:
python.version: '3.11'
Python312:
python.version: '3.12'
Python313:
python.version: '3.13'
Python314:
python.version: '3.14'
steps:
- task: UsePythonVersion@0
displayName: 'Use Python $(python.version)'
inputs:
versionSpec: '$(python.version)'
architecture: 'x64'

- script: 'python -m pip install uv'
displayName: 'Install uv'

- script: 'uv sync --upgrade --python "$(python.version)" --no-dev --group test'
displayName: 'Sync test environment'

- script: 'uv run --no-sync pytest tests --junitxml=junit/azure-iot-device-test-results.xml --cov=azure --cov-report=xml:coverage.xml --cov-report=html:coverage --cov-append'
displayName: pytest

- task: PublishTestResults@2
displayName: 'Publish Test Results **/*-test-results.xml'
inputs:
testResultsFiles: '**/*-test-results.xml'
testRunTitle: 'Python $(python.version)'
condition: always()

- task: PublishCodeCoverageResults@2
inputs:
summaryFileLocation: '$(Build.SourcesDirectory)/coverage.xml'
failIfCoverageEmpty: true

- job: 'Publish'
dependsOn:
- 'Test'
- 'Static_Analysis'

pool:
vmImage: 'ubuntu-24.04'

Expand All @@ -122,8 +63,20 @@ jobs:
- script: 'python -m pip install uv'
displayName: 'Install uv'

# Cache downloads without reusing the job environment.
- task: Cache@2
displayName: 'Cache uv'
inputs:
key: '"uv" | "$(Agent.OS)" | "build" | uv.lock'
restoreKeys: |
"uv" | "$(Agent.OS)" | "build"
"uv" | "$(Agent.OS)"
path: $(Pipeline.Workspace)/.uv-cache

- script: 'uv build'
displayName: Build source and wheel distribution
env:
UV_CACHE_DIR: $(Pipeline.Workspace)/.uv-cache

- task: PublishBuildArtifacts@1
displayName: 'Publish artifact dist'
Expand Down
53 changes: 35 additions & 18 deletions vsts/dps-e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,12 @@ stages:
pool:
vmImage: 'ubuntu-24.04'
steps:
- checkout: none
- script: |
sudo apt-get install -y uuid
AZURE_RESOURCE_GROUP="iotpythondpse2e$(uuid | tr -d '-')"
AZURE_IOTHUB_NAME="iotpythondpse2e$(uuid | tr -d '-')"
AZURE_DPS_NAME="iotpythondpse2e$(uuid | tr -d '-')"
RESOURCE_SUFFIX=$(tr -d '-' < /proc/sys/kernel/random/uuid)
AZURE_RESOURCE_GROUP="iotpythondpse2e$RESOURCE_SUFFIX"
AZURE_IOTHUB_NAME="iotpythondpse2e$RESOURCE_SUFFIX"
AZURE_DPS_NAME="iotpythondpse2e$RESOURCE_SUFFIX"

# Export variables to other stages/jobs/steps.
echo "##vso[task.setvariable variable=AZURE_RESOURCE_GROUP;isOutput=true]$AZURE_RESOURCE_GROUP"
Expand Down Expand Up @@ -178,8 +179,8 @@ stages:
echo "##vso[task.setvariable variable=DPS_ROOT_CA_CERT_PATH;isOutput=true]$DPS_ROOT_CA_CERT_PATH"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_PK_PATH;isOutput=true]$DPS_ROOT_CA_PK_PATH"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_CERT_BASE64;isOutput=true]$DPS_ROOT_CA_CERT_BASE64"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_PK_BASE64;isOutput=true]$DPS_ROOT_CA_PK_BASE64"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_PASSWORD;isOutput=true]$DPS_ROOT_CA_PASSWORD"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_PK_BASE64;isOutput=true;isSecret=true]$DPS_ROOT_CA_PK_BASE64"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_PASSWORD;isOutput=true;isSecret=true]$DPS_ROOT_CA_PASSWORD"
echo "##vso[task.setvariable variable=DPS_ROOT_CA_CONFIG_PATH;isOutput=true]$DPS_ROOT_CA_CONFIG_PATH"
name: testCertificates
displayName: Create Test Certificates
Expand All @@ -202,19 +203,22 @@ stages:
# Create Resource Group
az group create -g $AZURE_RESOURCE_GROUP -l $(AZURE_REGION)

# Create Azure IoT Hub
time az iot hub create -g $AZURE_RESOURCE_GROUP -n $AZURE_IOTHUB_NAME --dla false --mintls 1.2 --partition-count 4 --sku S1
# Create independent resources concurrently, then wait before linking them.
time az iot hub create -g $AZURE_RESOURCE_GROUP -n $AZURE_IOTHUB_NAME --dla false --mintls 1.2 --partition-count 4 --sku S1 &
iothub_create_pid=$!
time az iot dps create -g $AZURE_RESOURCE_GROUP --name $AZURE_DPS_NAME -l $(AZURE_REGION) &
dps_create_pid=$!

create_failed=0
wait "$iothub_create_pid" || create_failed=1
wait "$dps_create_pid" || create_failed=1
if (( create_failed )); then
exit 1
fi

export IOTHUB_CONNECTION_STRING=$(az iot hub connection-string show -g $AZURE_RESOURCE_GROUP --hub-name $AZURE_IOTHUB_NAME --pn iothubowner --query "connectionString" --output tsv)
export EVENTHUB_CONNECTION_STRING=$(az iot hub connection-string show -g $AZURE_RESOURCE_GROUP --hub-name $AZURE_IOTHUB_NAME --eh --query "connectionString" --output tsv)

for i in $(seq 1 11); do
cg_name="cg$i"
az iot hub consumer-group create -g "$AZURE_RESOURCE_GROUP" --hub-name "$AZURE_IOTHUB_NAME" --name "$cg_name"
done

# Create Device Provisioning service
time az iot dps create -g $AZURE_RESOURCE_GROUP --name $AZURE_DPS_NAME -l $(AZURE_REGION)
time az iot dps linked-hub create -g $AZURE_RESOURCE_GROUP --dps-name $AZURE_DPS_NAME --connection-string "$IOTHUB_CONNECTION_STRING"

export DPS_CONNECTION_STRING=$(az iot dps connection-string show -g $AZURE_RESOURCE_GROUP --dps-name $AZURE_DPS_NAME --policy-name provisioningserviceowner --query "connectionString" --output tsv)
Expand Down Expand Up @@ -245,9 +249,9 @@ stages:
time az iot dps certificate verify --dps-name $AZURE_DPS_NAME --resource-group $AZURE_RESOURCE_GROUP --name $DPS_ROOT_CA_CERT_NAME --path $verification_code_pem_path --etag $etag

# Export variables to other stages/jobs/steps.
echo "##vso[task.setvariable variable=IOTHUB_CONNECTION_STRING;isOutput=true]$IOTHUB_CONNECTION_STRING"
echo "##vso[task.setvariable variable=EVENTHUB_CONNECTION_STRING;isOutput=true]$EVENTHUB_CONNECTION_STRING"
echo "##vso[task.setvariable variable=DPS_CONNECTION_STRING;isOutput=true]$DPS_CONNECTION_STRING"
echo "##vso[task.setvariable variable=IOTHUB_CONNECTION_STRING;isOutput=true;isSecret=true]$IOTHUB_CONNECTION_STRING"
echo "##vso[task.setvariable variable=EVENTHUB_CONNECTION_STRING;isOutput=true;isSecret=true]$EVENTHUB_CONNECTION_STRING"
echo "##vso[task.setvariable variable=DPS_CONNECTION_STRING;isOutput=true;isSecret=true]$DPS_CONNECTION_STRING"
echo "##vso[task.setvariable variable=DPS_ID_SCOPE;isOutput=true]$DPS_ID_SCOPE"
echo "##vso[task.setvariable variable=DPS_HOSTNAME;isOutput=true]$DPS_HOSTNAME"
displayName: Create IoT Hub & DPS
Expand All @@ -273,11 +277,23 @@ stages:
versionSpec: '3.10'
architecture: 'x64'

# Cache downloads without reusing the job environment.
- task: Cache@2
displayName: 'Cache uv'
inputs:
key: '"uv" | "$(Agent.OS)" | "3.10" | uv.lock'
restoreKeys: |
"uv" | "$(Agent.OS)" | "3.10"
"uv" | "$(Agent.OS)"
path: $(Pipeline.Workspace)/.uv-cache

- script: 'python -m pip install uv'
displayName: 'Install uv'

- script: 'uv sync --upgrade --python 3.10 --no-dev --group test --group e2e'
displayName: 'Sync E2E environment'
env:
UV_CACHE_DIR: $(Pipeline.Workspace)/.uv-cache

- script: |
cd $(Agent.WorkFolder)
Expand Down Expand Up @@ -322,6 +338,7 @@ stages:
variables:
AZURE_RESOURCE_GROUP: $[stageDependencies.setup.create_azure_resources.outputs['setResourceNames.AZURE_RESOURCE_GROUP']]
steps:
- checkout: none
- task: AzureCLI@2
inputs:
azureSubscription: 'iot hub sdk service connection'
Expand Down
10 changes: 10 additions & 0 deletions vsts/horton-e2e.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
trigger:
branches:
include:
- main

pr:
branches:
include:
- main

variables:
Horton.FrameworkRoot: $(Agent.BuildDirectory)/e2e-fx
Horton.FrameworkRef: master
Expand Down
Loading