-
Notifications
You must be signed in to change notification settings - Fork 8
337 lines (311 loc) · 12.7 KB
/
Copy pathci.yml
File metadata and controls
337 lines (311 loc) · 12.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
name: CI
on:
push:
branches: [master]
pull_request:
branches: [master]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.14"
- name: Install linters
run: pip install ruff 'black<26'
- name: Run ruff check
run: ruff check plugins/ tests/ utils/
- name: Run black check
run: black --check plugins/ tests/ utils/
# Scoped to this file on purpose: test_pipelines.py raises on import when
# gst-launch-1.0 is missing, which would fail collection for the whole dir.
- name: Run g2g backend tests
run: |
pip install pytest numpy
pytest tests/test_g2g_backend.py -q
# each leg installs one engine's extra so their pins never have to agree
engines:
needs: lint
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- engine: onnx
extra: onnx
- engine: openvino
extra: openvino
- engine: tensorflow
extra: tensorflow
- engine: tflite
extra: tensorflow
- engine: ncnn
extra: ncnn
- engine: executorch
extra: executorch
- engine: iree
extra: iree
- engine: pytorch
extra: onnx
- engine: llamacpp
extra: llamacpp
- engine: tvm
extra: tvm
- engine: mlx
extra: mlx-cpu
- engine: jax
extra: jax-cpu
- engine: tinygrad
extra: tinygrad
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.12"
- name: Cache model downloads
uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.cache/huggingface
~/.config/Ultralytics
key: engines-${{ matrix.engine }}-${{ hashFiles('pyproject.toml', '.github/workflows/ci.yml') }}
- name: Install the engine
env:
# the cached llama.cpp wheel must run on whichever cpu the next runner has
CMAKE_ARGS: -DGGML_NATIVE=OFF
run: |
# pygobject and pycairo build from source, this job has no gstreamer step to pull their headers in
sudo apt-get update
sudo apt-get install -y libcairo2-dev libgirepository-2.0-dev libglib2.0-dev pkg-config
pip install -e ".[${{ matrix.extra }},yolo]" pytest
- name: Run the engine tests
env:
PYML_REQUIRE_ENGINE: ${{ matrix.engine }}
run: python -m pytest tests/test_engines.py -q -k "${{ matrix.engine }}"
build:
needs: lint
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
python-version: ["3.12", "3.14"]
exclude:
# ubuntu-latest is 24.04 with system Python 3.12;
# GStreamer gi bindings are built for 3.12 only
- os: ubuntu-latest
python-version: "3.14"
include:
- os: ubuntu-latest
# the gst python plugin loader links the system libpython
venv-python: /usr/bin/python3
gst-install: |
sudo apt-get update
sudo apt-get install -y \
gstreamer1.0-tools \
gstreamer1.0-plugins-base \
gstreamer1.0-plugins-good \
gstreamer1.0-plugins-bad \
gstreamer1.0-libav \
gir1.2-gstreamer-1.0 \
gir1.2-gst-plugins-base-1.0 \
gir1.2-gst-plugins-bad-1.0 \
libgstreamer-plugins-bad1.0-dev \
python3-gi \
python3-gi-cairo \
python3-gst-1.0 \
python3-venv \
gstreamer1.0-python3-plugin-loader \
libcairo2 \
libcairo2-dev \
pkg-config \
libgirepository-2.0-dev \
libglib2.0-dev \
python3-dev
pip install numpy opencv-python-headless ultralytics onnxruntime
- os: macos-latest
gst-install: |
brew install gstreamer cairo pkg-config pygobject3 gobject-introspection
echo "DYLD_LIBRARY_PATH=$(brew --prefix)/lib:$DYLD_LIBRARY_PATH" >> $GITHUB_ENV
echo "GI_TYPELIB_PATH=$(brew --prefix)/lib/girepository-1.0" >> $GITHUB_ENV
- os: windows-latest
gst-install: |
choco install gstreamer gstreamer-devel --yes
"C:\gstreamer\1.0\msvc_x86_64\bin" >> $env:GITHUB_PATH
"GST_PLUGIN_PATH=C:\gstreamer\1.0\msvc_x86_64\lib\gstreamer-1.0" >> $env:GITHUB_ENV
"PKG_CONFIG_PATH=C:\gstreamer\1.0\msvc_x86_64\lib\pkgconfig" >> $env:GITHUB_ENV
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install GStreamer
run: ${{ matrix.gst-install }}
- name: Create venv and install CPU dependencies
shell: bash
# pygobject builds from source and choco gstreamer-devel ships no pkg-config
continue-on-error: ${{ matrix.os == 'windows-latest' }}
run: |
${{ matrix.venv-python || 'python' }} -m venv --system-site-packages .venv
if [[ "$RUNNER_OS" == "Windows" ]]; then
source .venv/Scripts/activate
else
source .venv/bin/activate
fi
pip install -e ".[yolo,mqtt,mcp,audio]"
- name: Verify core imports
shell: bash
continue-on-error: true
run: |
if [[ "$RUNNER_OS" == "Windows" ]]; then
source .venv/Scripts/activate
else
source .venv/bin/activate
fi
unset LD_LIBRARY_PATH
python -c "
import gi
gi.require_version('Gst', '1.0')
gi.require_version('GstBase', '1.0')
from gi.repository import Gst
Gst.init(None)
import importlib, sys
modules = [
'plugins.python.streammux',
'plugins.python.streamdemux',
'plugins.python.overlay',
'plugins.python.engine.ml_engine',
'plugins.python.engine.engine_factory',
]
failed = []
for m in modules:
try:
importlib.import_module(m)
print(f' OK {m}')
except Exception as e:
print(f' FAIL {m}: {e}')
failed.append(m)
if failed:
print(f'\n{len(failed)} import(s) failed')
sys.exit(1)
print('\nAll imports OK')
"
- name: Check GStreamer plugin discovery (Linux)
if: runner.os == 'Linux'
run: |
source .venv/bin/activate
VENV_SITE=$(python -c "import site; print(site.getsitepackages()[0])")
# libgstpython.so uses system Python which needs:
# - /usr/lib/python3/dist-packages for gi (python3-gi apt package)
# - venv site-packages for pip-installed ML packages
# - plugins/python for our GStreamer element modules
export PYTHONPATH="/usr/lib/python3/dist-packages:${VENV_SITE}:${{ github.workspace }}/plugins/python"
export GST_PLUGIN_PATH=${{ github.workspace }}/plugins
# Prevent hostedtoolcache libpython from shadowing system libpython
unset LD_LIBRARY_PATH
rm -rf ~/.cache/gstreamer-1.0
gst-inspect-1.0 python
gst-inspect-1.0 pyml_objectdetector
- name: Run gst backend tests (Linux)
if: runner.os == 'Linux'
run: |
source .venv/bin/activate
pip install pytest
unset LD_LIBRARY_PATH
rm -rf ~/.cache/gstreamer-1.0
# PIPELINES.md pipelines need the GPU and the data dir, the other two load models
python -m pytest tests/ -q \
--ignore=tests/test_pipelines.py \
--ignore=tests/test_zeroshotdetector.py \
--ignore=tests/test_engines.py
- name: Cache the PIPELINES.md models (Linux)
if: runner.os == 'Linux'
uses: actions/cache@v4
with:
path: |
~/.cache/huggingface
~/.cache/torch
~/.config/Ultralytics
key: pipelines-${{ hashFiles('PIPELINES.md') }}
# the alert pipeline publishes to a local broker
- name: Run the PIPELINES.md pipelines a headless runner can (Linux)
if: runner.os == 'Linux'
run: |
source .venv/bin/activate
unset LD_LIBRARY_PATH
sudo apt-get install -y mosquitto
HEADLESS_PIPELINES=1 PIPELINE_TIMEOUT=300 GST_DEBUG=2 python -m pytest tests/test_pipelines.py -q
- name: Upload the pipeline logs (Linux)
if: runner.os == 'Linux' && failure()
uses: actions/upload-artifact@v7
with:
name: pipeline-logs
path: tests/logs
- name: ONNX pipeline smoke test (Linux)
if: runner.os == 'Linux'
run: |
source .venv/bin/activate
pip install onnxruntime
VENV_SITE=$(python -c "import site; print(site.getsitepackages()[0])")
export PYTHONPATH="/usr/lib/python3/dist-packages:${VENV_SITE}:${{ github.workspace }}/plugins/python"
export GST_PLUGIN_PATH=${{ github.workspace }}/plugins
unset LD_LIBRARY_PATH
rm -rf ~/.cache/gstreamer-1.0
# Export YOLO model to ONNX
python -c "from ultralytics import YOLO; YOLO('yolo11n.pt').export(format='onnx')"
# Run 2-frame smoke test with synthetic source (avoids codec deps)
gst-launch-1.0 videotestsrc num-buffers=2 \
! "video/x-raw,format=RGB,width=640,height=640" \
! pyml_objectdetector engine-name=onnx model-name=yolo11n.onnx device=cpu input-format=nchw post-process=anchor_free \
! fakesink
- name: ONNX pipeline smoke test (macOS)
if: runner.os == 'macOS'
run: |
# brew's libgstpython.dylib embeds brew's Python (not actions/setup-python).
# Install ML packages into brew's Python so libgstpython can find them.
BREW_PREFIX=$(brew --prefix)
BREW_PIP="${BREW_PREFIX}/bin/pip3"
BREW_PYTHON="${BREW_PREFIX}/bin/python3"
${BREW_PIP} install --break-system-packages ultralytics onnxruntime onnx numpy opencv-python-headless
export GST_PLUGIN_PATH=${{ github.workspace }}/plugins
export PYTHONPATH=${{ github.workspace }}/plugins/python
# Export YOLO model to ONNX
${BREW_PYTHON} -c "from ultralytics import YOLO; YOLO('yolo11n.pt').export(format='onnx')"
# Run 2-frame smoke test with synthetic source (avoids codec deps)
gst-launch-1.0 videotestsrc num-buffers=2 \
! "video/x-raw,format=RGB,width=640,height=640" \
! pyml_objectdetector engine-name=onnx model-name=yolo11n.onnx device=cpu input-format=nchw post-process=anchor_free \
! fakesink
- name: ONNX pipeline smoke test (Windows)
if: runner.os == 'Windows'
continue-on-error: true
shell: bash
run: |
source .venv/Scripts/activate
pip install onnxruntime
SITE_PKGS=$(python -c "import site; print(site.getsitepackages()[0])")
# Find GStreamer install location
GST_BIN=$(find /c/gstreamer -name "gst-launch-1.0.exe" 2>/dev/null | head -1 | xargs dirname)
if [[ -z "$GST_BIN" ]]; then
echo "ERROR: gst-launch-1.0.exe not found under /c/gstreamer"
find /c/gstreamer -type f -name "gst-*" 2>/dev/null || echo "No gst binaries found"
ls /c/gstreamer/ 2>/dev/null || echo "/c/gstreamer does not exist"
exit 1
fi
echo "Found GStreamer at: $GST_BIN"
export PATH="${GST_BIN}:$PATH"
GST_LIB=$(dirname "$GST_BIN")/lib/gstreamer-1.0
export GST_PLUGIN_PATH="${{ github.workspace }}/plugins;${GST_LIB}"
export PYTHONPATH="${SITE_PKGS};${{ github.workspace }}/plugins/python;$PYTHONPATH"
# Export YOLO model to ONNX
python -c "from ultralytics import YOLO; YOLO('yolo11n.pt').export(format='onnx')"
# Run 2-frame smoke test with synthetic source (avoids codec deps)
gst-launch-1.0.exe videotestsrc num-buffers=2 \
! "video/x-raw,format=RGB,width=640,height=640" \
! pyml_objectdetector engine-name=onnx model-name=yolo11n.onnx device=cpu input-format=nchw post-process=anchor_free \
! fakesink