Skip to content

Commit 528cbf9

Browse files
authored
Merge branch 'dev' into xml_set_fanspeed
2 parents 4c6e862 + 5316769 commit 528cbf9

28 files changed

Lines changed: 1172 additions & 770 deletions

.devcontainer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
"ghcr.io/devcontainers/features/docker-in-docker:2": {},
4848
"ghcr.io/devcontainers/features/rust:1": {}
4949
},
50-
"image": "mcr.microsoft.com/devcontainers/base:debian",
50+
"image": "mcr.microsoft.com/devcontainers/python:3",
5151
"name": "Deebot client",
5252
// Use 'forwardPorts' to make a list of ports inside the container available locally.
5353
// "forwardPorts": [],

.github/workflows/ci.yml

Lines changed: 278 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,58 @@
11
name: CI
2+
run-name: "${{ github.event_name == 'workflow_dispatch' && format('CI: {0}', github.ref_name) || '' }}"
23

34
on:
45
push:
56
branches:
67
- dev
78
pull_request:
9+
release:
10+
types: [published]
11+
workflow_dispatch:
812

913
env:
10-
UV_CACHE_DIR: /tmp/.uv-cache
14+
ADDITIONAL_PYTHON_VERSIONS: "['3.12']"
15+
16+
concurrency:
17+
group: ${{ github.workflow }}-${{ (github.event_name == 'release' && github.run_id) || github.event.pull_request.number || github.ref }}
18+
cancel-in-progress: true
1119

1220
jobs:
21+
info:
22+
name: Collect information & prepare
23+
outputs:
24+
default_python_version: ${{ steps.info.outputs.default_python_version }}
25+
python_versions: ${{ steps.info.outputs.python_versions }}
26+
runs-on: ubuntu-latest
27+
steps:
28+
- name: ⤵️ Check out code from GitHub
29+
uses: actions/checkout@v4.2.2
30+
- name: Collect information
31+
id: info
32+
run: |
33+
default_python_version=$(cat .python-version)
34+
echo "default python version: ${default_python_version}"
35+
echo "default_python_version=${default_python_version}" >> $GITHUB_OUTPUT
36+
37+
# Check if ADDITIONAL_PYTHON_VERSIONS contains default_python_version. If not add it
38+
if [[ ! ${ADDITIONAL_PYTHON_VERSIONS} == *"${default_python_version}"* ]]; then
39+
ADDITIONAL_PYTHON_VERSIONS="[${ADDITIONAL_PYTHON_VERSIONS:1:-1}, '${default_python_version}']"
40+
fi
41+
echo "python_versions: ${ADDITIONAL_PYTHON_VERSIONS}"
42+
echo "python_versions=${ADDITIONAL_PYTHON_VERSIONS}" >> $GITHUB_OUTPUT
43+
- name: 🏗 Install uv
44+
uses: astral-sh/setup-uv@v5
45+
with:
46+
enable-cache: true
47+
- name: Generate requirements.txt
48+
run: |
49+
uv export --no-hashes --no-dev --group test --no-emit-project > requirements.txt
50+
- name: Upload requirements.txt
51+
uses: actions/upload-artifact@v4
52+
with:
53+
name: requirements.txt
54+
path: requirements.txt
55+
1356
code-quality:
1457
runs-on: "ubuntu-latest"
1558
name: Check code quality
@@ -37,68 +80,273 @@ jobs:
3780
- name: Verify no getLogger usages
3881
run: scripts/check_getLogger.sh
3982

40-
tests:
41-
runs-on: "ubuntu-latest"
42-
name: Run tests
83+
build-test-native:
84+
name: ${{ matrix.platform.name }} ${{ matrix.platform.target }} ${{ matrix.python-version }}
85+
runs-on: ${{ matrix.platform.runner }}
86+
needs:
87+
- info
88+
- code-quality
4389
strategy:
90+
fail-fast: false
4491
matrix:
45-
python-version:
46-
- "3.12"
47-
- "3.13"
48-
env:
49-
# To enable backtrace in case of a crash
50-
RUST_BACKTRACE: 1
92+
include:
93+
- name: Linux
94+
manylinux: manylinux_2_34
95+
- name: Macos
96+
pytest_args: -m "not docker"
97+
platform:
98+
- name: Linux
99+
runner: ubuntu-latest
100+
target: x86_64
101+
- name: Linux
102+
runner: ubuntu-24.04-arm
103+
target: aarch64
104+
- name: Windows
105+
runner: windows-latest
106+
target: x64
107+
- name: Macos
108+
runner: macos-13
109+
target: x86_64
110+
- name: Macos
111+
runner: macos-14
112+
target: aarch64
113+
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
51114
steps:
52-
- name: ⤵️ Checkout repository
53-
uses: actions/checkout@v4
54-
55-
- name: 🏗 Install uv and Python ${{ matrix.python-version }}
115+
- uses: actions/checkout@v4
116+
- uses: actions/setup-python@v5
117+
with:
118+
python-version: ${{ matrix.python-version }}
119+
architecture: ${{ (matrix.platform.name == 'Windows') && matrix.platform.target || '' }}
120+
- name: 🏗 Set package version
121+
if: ${{ matrix.platform.name != 'Windows' && github.event_name == 'release' }}
122+
run: |
123+
sed -i.bak "s/^version = \".*\"/version = \"${{ github.event.release.tag_name }}\"/" pyproject.toml && rm pyproject.toml.bak
124+
- name: 🏗 Set package version (Windows)
125+
if: ${{ matrix.platform.name == 'Windows' && github.event_name == 'release' }}
126+
run: |
127+
(Get-Content pyproject.toml) -replace 'version = ".*"', 'version = "${{ github.event.release.tag_name }}"' | Set-Content pyproject.toml
128+
- name: Build wheels
129+
uses: PyO3/maturin-action@v1
130+
with:
131+
target: ${{ matrix.platform.target }}
132+
args: --release --strip --out dist -i python${{ matrix.python-version }}
133+
sccache: "false"
134+
manylinux: ${{ matrix.manylinux }}
135+
before-script-linux: python3 -m ensurepip
136+
- name: Upload wheels
137+
uses: actions/upload-artifact@v4
138+
with:
139+
name: wheels-${{ matrix.platform.name }}-${{ matrix.platform.target }}-${{ matrix.python-version }}
140+
path: dist
141+
- name: 🏗 Install uv
56142
uses: astral-sh/setup-uv@v5
57143
with:
58144
enable-cache: true
59-
cache-dependency-glob: "uv.lock"
60-
cache-local-path: ${{ env.UV_CACHE_DIR }}
61-
python-version: ${{ matrix.python-version }}
62-
63-
- name: 🏗 Install the project
64-
run: uv sync --locked --group test
65-
145+
- name: Download requirements.txt
146+
uses: actions/download-artifact@v4
147+
with:
148+
name: requirements.txt
149+
- name: Prepare env and install wheel
150+
shell: bash
151+
run: |
152+
# Remove sources otherwise pytest will complain about missing rust files
153+
rm -rf deebot_client
154+
uv venv -p ${{ matrix.python-version }}
155+
uv pip install -r requirements.txt
156+
uv pip install --force-reinstall dist/deebot_client-*.whl
66157
- name: Run pytest
67-
run: uv run --frozen pytest tests --cov=./ --cov-report=xml --junitxml=junit.xml -o junit_family=legacy
68-
158+
if: ${{ matrix.platform.name != 'Windows' }}
159+
id: pytest
160+
run: |
161+
source .venv/bin/activate
162+
site_packages=$(python -c "import site; print(site.getsitepackages()[0])")
163+
echo "site-packages: ${site_packages}"
164+
pytest tests --cov=${site_packages}/deebot_client --cov-report=xml --junitxml=junit.xml -o junit_family=legacy -v ${{ matrix.pytest_args || ''}}
165+
- name: Run pytest (windows)
166+
if: ${{ matrix.platform.name == 'Windows' }}
167+
id: pytest_windows
168+
run: |
169+
.venv/Scripts/activate
170+
$sitePackages = python -c "import site; print(site.getsitepackages()[0])"
171+
Write-Output "site-packages: $sitePackages"
172+
pytest tests --cov="$sitePackages/deebot_client" --cov-report=xml --junitxml=junit.xml -o junit_family=legacy -v -m "not docker"
69173
- name: Upload coverage to Codecov
70174
uses: codecov/codecov-action@v5
175+
env:
176+
PYTHON: ${{ matrix.python-version }}
177+
TARGET: ${{ matrix.platform.target }}
178+
PLATFORM: ${{ matrix.platform.name }}
71179
with:
72180
fail_ci_if_error: true
181+
name: ${{ matrix.platform.name }} ${{ matrix.platform.target }} ${{ matrix.python-version }}
182+
env_vars: PLATFORM,TARGET,PYTHON
183+
- name: Upload test results to Codecov
184+
if: ${{ !cancelled() && ((matrix.platform.name == 'Windows' && steps.pytest_windows.outcome) || steps.pytest.outcome) == 'success' }}
185+
uses: codecov/test-results-action@v1
186+
with:
187+
# fail_ci_if_error: true
188+
verbose: true
73189

190+
build-test-musl:
191+
name: Musllinux ${{ matrix.platform.target }} ${{ matrix.python-version }}
192+
runs-on: ubuntu-latest
193+
needs:
194+
- info
195+
- code-quality
196+
strategy:
197+
fail-fast: false
198+
matrix:
199+
platform:
200+
- target: x86_64
201+
docker_arch: amd64
202+
# - target: aarch64
203+
# docker_arch: arm64
204+
python-version: ${{ fromJSON(needs.info.outputs.python_versions) }}
205+
steps:
206+
- uses: actions/checkout@v4
207+
- uses: actions/setup-python@v5
208+
with:
209+
python-version: ${{ matrix.python-version }}
210+
- name: 🏗 Set package version
211+
if: ${{ github.event_name == 'release' }}
212+
run: |
213+
sed -i "s/^version = \".*\"/version = \"${{ github.event.release.tag_name }}\"/" pyproject.toml
214+
- name: Build wheels
215+
uses: PyO3/maturin-action@v1
216+
with:
217+
target: ${{ matrix.platform.target }}
218+
args: --release --strip --out dist -i python${{ matrix.python-version }}
219+
sccache: "false"
220+
manylinux: musllinux_1_2
221+
- name: Upload wheels
222+
uses: actions/upload-artifact@v4
223+
with:
224+
name: wheels-musllinux-${{ matrix.platform.target }}-${{ matrix.python-version }}
225+
path: dist
226+
- name: Download requirements.txt
227+
uses: actions/download-artifact@v4
228+
with:
229+
name: requirements.txt
230+
- name: Install Qemu
231+
uses: docker/setup-qemu-action@v3
232+
- name: Install Docker Buildx
233+
uses: docker/setup-buildx-action@v3
234+
- name: Build image
235+
uses: docker/build-push-action@v6
236+
with:
237+
context: .
238+
file: ci/Dockerfile.alpine
239+
platforms: linux/${{ matrix.platform.docker_arch }}
240+
push: false
241+
load: true
242+
cache-from: type=gha
243+
cache-to: type=gha,mode=max
244+
build-args: |
245+
PYTHON_VERSION=${{ matrix.python-version }}
246+
tags: deebot_client:${{ matrix.platform.target }}-${{ matrix.python-version }}
247+
- name: Pytest in docker
248+
id: pytest
249+
run: |
250+
docker run --rm -v ${{ github.workspace }}:/github/workspace --platform linux/${{ matrix.platform.docker_arch }} deebot_client:${{ matrix.platform.target }}-${{ matrix.python-version }}
251+
- name: Upload coverage to Codecov
252+
uses: codecov/codecov-action@v5
253+
env:
254+
PYTHON: ${{ matrix.python-version }}
255+
TARGET: ${{ matrix.platform.target }}
256+
PLATFORM: Musllinux
257+
with:
258+
fail_ci_if_error: true
259+
name: Musllinux ${{ matrix.platform.target }} ${{ matrix.python-version }}
260+
env_vars: PLATFORM,TARGET,PYTHON
74261
- name: Upload test results to Codecov
75-
if: ${{ !cancelled() }}
262+
if: ${{ !cancelled() && steps.pytest.outcome == 'success' }}
76263
uses: codecov/test-results-action@v1
77264
with:
78265
fail_ci_if_error: true
79266

80267
benchmarks:
81268
runs-on: "ubuntu-latest"
82269
name: Run benchmarks
270+
if: ${{ github.event_name != 'release' }}
271+
needs:
272+
- build-test-native
273+
- build-test-musl
274+
- sdist
83275
steps:
84276
- name: ⤵️ Checkout repository
85277
uses: actions/checkout@v4
86-
87278
- name: 🏗 Install uv
88279
uses: astral-sh/setup-uv@v5
89-
90280
- name: 🏗 Setup Python
91281
uses: actions/setup-python@v5
92282
with:
93-
python-version-file: ".python-version"
94-
283+
python-version: ${{ needs.info.outputs.default_python_version }}
284+
- name: Download requirements.txt
285+
uses: actions/download-artifact@v4
286+
with:
287+
name: requirements.txt
95288
- name: 🏗 Install the project
96289
run: |
97-
uv export --group benchmark > requirements.txt
98290
uv pip install -e . --system -r requirements.txt
99-
100291
- name: Run benchmarks
101292
uses: CodSpeedHQ/action@main
102293
with:
103294
run: pytest tests/ --codspeed
104295
token: ${{ secrets.CODSPEED_TOKEN }}
296+
297+
sdist:
298+
name: Create source distribution
299+
runs-on: ubuntu-latest
300+
needs: code-quality
301+
steps:
302+
- name: ⤵️ Check out code from GitHub
303+
uses: actions/checkout@v4.2.2
304+
- name: 🏗 Set up uv
305+
uses: astral-sh/setup-uv@v5
306+
with:
307+
enable-cache: true
308+
- name: 🏗 Set package version
309+
if: ${{ github.event_name == 'release' }}
310+
run: |
311+
sed -i "s/^version = \".*\"/version = \"${{ github.event.release.tag_name }}\"/" pyproject.toml
312+
- name: 📦 Build source package
313+
run: uv build --sdist
314+
- name: Upload sdist
315+
uses: actions/upload-artifact@v4
316+
with:
317+
name: wheels-sdist
318+
path: dist
319+
320+
release:
321+
name: Releasing to PyPi
322+
if: ${{ github.event_name == 'release' }}
323+
runs-on: ubuntu-latest
324+
needs:
325+
- build-test-native
326+
- build-test-musl
327+
- sdist
328+
environment:
329+
name: release
330+
url: https://pypi.org/p/deebot-client
331+
permissions:
332+
contents: write
333+
id-token: write
334+
steps:
335+
- name: ⤵️ Check out code from GitHub
336+
uses: actions/checkout@v4.2.2
337+
- name: ⬇️ Download wheels
338+
uses: actions/download-artifact@v4
339+
with:
340+
pattern: wheels-*
341+
path: dist
342+
merge-multiple: true
343+
- name: 🚀 Publish to PyPi
344+
uses: pypa/gh-action-pypi-publish@v1.12.4
345+
with:
346+
verbose: true
347+
print-hash: true
348+
- name: ✍️ Sign published artifacts
349+
uses: sigstore/gh-action-sigstore-python@v3.0.0
350+
with:
351+
inputs: ./dist/*.tar.gz ./dist/*.whl
352+
release-signing-artifacts: true

0 commit comments

Comments
 (0)