Skip to content

Commit d61e5e8

Browse files
committed
ci: add debian package build workflow for tagged releases
Adds .github/workflows/debian-package.yml which builds .deb packages for amd64, arm64, and armhf on every tagged release and uploads them to the corresponding GitHub Release. - Builds inside debian:sid-slim (same as Dockerfile) to guarantee FFmpeg 8.x ABI compatibility - Bundles custom-built libs (libuv, llhttp, libsqlite3, libsod) under /usr/lib/lightnvr/ with an ld.so.conf.d drop-in for clean resolution - Proper Debian packaging: conffiles, postinst, prerm, postrm - arm64 uses the native ubuntu-24.04-arm runner; armhf uses QEMU on that same runner (faster than QEMU on x86_64) - upload-to-release job attaches all three .deb files to the GitHub Release created by the changelog workflow Closes #344
1 parent e708df3 commit d61e5e8

1 file changed

Lines changed: 272 additions & 0 deletions

File tree

Lines changed: 272 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,272 @@
1+
name: Build Debian Package
2+
3+
on:
4+
push:
5+
tags:
6+
- '*.*.*'
7+
workflow_dispatch:
8+
9+
permissions:
10+
contents: write
11+
packages: read
12+
13+
jobs:
14+
build-deb:
15+
name: Build .deb (${{ matrix.deb_arch }})
16+
runs-on: ${{ matrix.runner }}
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
include:
21+
- runner: ubuntu-latest
22+
platform: linux/amd64
23+
deb_arch: amd64
24+
- runner: ubuntu-24.04-arm
25+
platform: linux/arm64
26+
deb_arch: arm64
27+
- runner: ubuntu-24.04-arm
28+
platform: linux/arm/v7
29+
deb_arch: armhf
30+
31+
steps:
32+
- name: Checkout
33+
uses: actions/checkout@v5
34+
with:
35+
submodules: recursive
36+
37+
- name: Extract version from tag
38+
id: version
39+
run: |
40+
TAG="${{ github.ref_name }}"
41+
VERSION="${TAG#v}"
42+
echo "version=$VERSION" >> $GITHUB_OUTPUT
43+
44+
- name: Set up QEMU
45+
if: matrix.platform == 'linux/arm/v7'
46+
uses: docker/setup-qemu-action@v4
47+
48+
- name: Set up Docker Buildx
49+
uses: docker/setup-buildx-action@v4
50+
51+
- name: Build Docker image
52+
uses: docker/build-push-action@v7
53+
with:
54+
context: .
55+
platforms: ${{ matrix.platform }}
56+
push: false
57+
load: true
58+
tags: lightnvr-pkg:local
59+
60+
- name: Extract files and create .deb package
61+
run: |
62+
VERSION="${{ steps.version.outputs.version }}"
63+
DEB_ARCH="${{ matrix.deb_arch }}"
64+
PKG_NAME="lightnvr_${VERSION}_${DEB_ARCH}"
65+
PKG_DIR="$(pwd)/pkg/${PKG_NAME}"
66+
67+
# ── Directory structure ──────────────────────────────────────────────
68+
mkdir -p "${PKG_DIR}/DEBIAN"
69+
mkdir -p "${PKG_DIR}/usr/bin"
70+
mkdir -p "${PKG_DIR}/etc/ld.so.conf.d"
71+
mkdir -p "${PKG_DIR}/etc/lightnvr/go2rtc"
72+
mkdir -p "${PKG_DIR}/var/lib/lightnvr/www"
73+
mkdir -p "${PKG_DIR}/var/lib/lightnvr/data"
74+
mkdir -p "${PKG_DIR}/var/log/lightnvr"
75+
mkdir -p "${PKG_DIR}/var/run/lightnvr"
76+
mkdir -p "${PKG_DIR}/usr/share/lightnvr/migrations"
77+
mkdir -p "${PKG_DIR}/usr/lib/lightnvr"
78+
mkdir -p "${PKG_DIR}/lib/systemd/system"
79+
80+
# ── Extract image filesystem ─────────────────────────────────────────
81+
CID=$(docker create lightnvr-pkg:local)
82+
trap "docker rm -f '${CID}' 2>/dev/null || true" EXIT
83+
84+
# Binaries
85+
docker cp "${CID}:/bin/lightnvr" "${PKG_DIR}/usr/bin/lightnvr"
86+
docker cp "${CID}:/bin/go2rtc" "${PKG_DIR}/usr/bin/go2rtc"
87+
88+
# Custom-built libraries (bundle to guarantee ABI compatibility)
89+
mkdir -p /tmp/lightnvr_libs
90+
docker export "${CID}" | tar xf - --wildcards \
91+
'usr/lib/libuv.so*' \
92+
'usr/lib/libllhttp.so*' \
93+
'usr/lib/libsqlite3.so*' \
94+
'lib/libsod.so*' \
95+
-C /tmp/lightnvr_libs/ 2>/dev/null || true
96+
97+
find /tmp/lightnvr_libs -type f -name '*.so*' ! -type l \
98+
-exec install -m 644 {} "${PKG_DIR}/usr/lib/lightnvr/" \;
99+
100+
# Recreate symlinks inside the package directory
101+
(cd "${PKG_DIR}/usr/lib/lightnvr" && \
102+
for f in libuv.so.1.0.0 libllhttp.so.9.3.1 libsod.so.1.1.9; do \
103+
[ -f "$f" ] || continue; \
104+
BASE="${f%%.*}"; \
105+
SONAME=$(readlink -f /tmp/lightnvr_libs/usr/lib/${f} 2>/dev/null | xargs -I{} basename {} 2>/dev/null || echo ""); \
106+
ln -sf "$f" "${BASE}.so" 2>/dev/null || true; \
107+
done; \
108+
[ -f libsod.so.1.1.9 ] && ln -sf libsod.so.1.1.9 libsod.so.1 && ln -sf libsod.so.1 libsod.so || true; \
109+
[ -f libuv.so.1.0.0 ] && ln -sf libuv.so.1.0.0 libuv.so.1 && ln -sf libuv.so.1 libuv.so || true; \
110+
[ -f libllhttp.so.9.3.1 ] && ln -sf libllhttp.so.9.3.1 libllhttp.so.9 && ln -sf libllhttp.so.9 libllhttp.so || true)
111+
112+
# Find and symlink sqlite3 if extracted
113+
SQLITE_FILE=$(find "${PKG_DIR}/usr/lib/lightnvr" -name 'libsqlite3.so.*' ! -type l | head -1)
114+
if [ -n "${SQLITE_FILE}" ]; then
115+
BASE=$(basename "${SQLITE_FILE}")
116+
(cd "${PKG_DIR}/usr/lib/lightnvr" && \
117+
ln -sf "${BASE}" libsqlite3.so.0 2>/dev/null || true && \
118+
ln -sf libsqlite3.so.0 libsqlite3.so 2>/dev/null || true)
119+
fi
120+
121+
# Web assets, migrations, config
122+
docker cp "${CID}:/var/lib/lightnvr/www/." "${PKG_DIR}/var/lib/lightnvr/www/"
123+
docker cp "${CID}:/usr/share/lightnvr/migrations/." "${PKG_DIR}/usr/share/lightnvr/migrations/"
124+
docker cp "${CID}:/etc/lightnvr/." "${PKG_DIR}/etc/lightnvr/"
125+
126+
# ── ldconfig drop-in ────────────────────────────────────────────────
127+
echo "/usr/lib/lightnvr" > "${PKG_DIR}/etc/ld.so.conf.d/lightnvr.conf"
128+
129+
# ── systemd service ─────────────────────────────────────────────────
130+
cat > "${PKG_DIR}/lib/systemd/system/lightnvr.service" << 'SYSTEMD_EOF'
131+
[Unit]
132+
Description=LightNVR - Lightweight Network Video Recorder
133+
After=network.target network-online.target
134+
Wants=network-online.target
135+
136+
[Service]
137+
Type=forking
138+
PIDFile=/var/run/lightnvr.pid
139+
User=root
140+
Group=root
141+
Environment="PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
142+
Environment="HOME=/root"
143+
ExecStart=/usr/bin/lightnvr -c /etc/lightnvr/lightnvr.ini -d
144+
ExecReload=/bin/kill -HUP $MAINPID
145+
KillMode=mixed
146+
KillSignal=SIGTERM
147+
TimeoutStartSec=30
148+
TimeoutStopSec=30
149+
Restart=on-failure
150+
RestartSec=5
151+
ExecStartPre=/bin/mkdir -p /var/lib/lightnvr/data /var/log/lightnvr /var/run/lightnvr
152+
StandardOutput=journal
153+
StandardError=journal
154+
SyslogIdentifier=lightnvr
155+
156+
[Install]
157+
WantedBy=multi-user.target
158+
SYSTEMD_EOF
159+
# Un-indent the service file (heredoc indented for readability)
160+
sed -i 's/^ //' "${PKG_DIR}/lib/systemd/system/lightnvr.service"
161+
162+
# ── DEBIAN/control ──────────────────────────────────────────────────
163+
INSTALLED_SIZE=$(du -sk "${PKG_DIR}" | cut -f1)
164+
cat > "${PKG_DIR}/DEBIAN/control" << CONTROL_EOF
165+
Package: lightnvr
166+
Version: ${VERSION}
167+
Architecture: ${DEB_ARCH}
168+
Maintainer: OpenSensor <support@opensensor.io>
169+
Installed-Size: ${INSTALLED_SIZE}
170+
Depends: ffmpeg, libavcodec62 | libavcodec-extra, libavformat62, libavutil60, libswscale9, libcurl4t64 | libcurl4, libmbedtls21, libmosquitto1, libcjson1, procps
171+
Section: net
172+
Priority: optional
173+
Homepage: https://github.com/opensensor/lightNVR
174+
Description: Lightweight Network Video Recorder
175+
LightNVR is a lightweight Network Video Recorder designed for recording,
176+
managing, and streaming video from IP cameras via RTSP.
177+
.
178+
Features: RTSP recording, WebRTC streaming (go2rtc), object detection
179+
(SOD), ONVIF discovery, MQTT integration, and a web-based UI.
180+
.
181+
Note: This package targets Debian sid. Ubuntu users are encouraged to
182+
use the official Docker image (ghcr.io/opensensor/lightnvr).
183+
CONTROL_EOF
184+
sed -i 's/^ //' "${PKG_DIR}/DEBIAN/control"
185+
186+
# ── DEBIAN/conffiles ────────────────────────────────────────────────
187+
printf '/etc/lightnvr/lightnvr.ini\n/etc/lightnvr/go2rtc/go2rtc.yaml\n' \
188+
> "${PKG_DIR}/DEBIAN/conffiles"
189+
190+
# ── DEBIAN/postinst ─────────────────────────────────────────────────
191+
cat > "${PKG_DIR}/DEBIAN/postinst" << 'POSTINST_EOF'
192+
#!/bin/sh
193+
set -e
194+
ldconfig
195+
if command -v systemctl >/dev/null 2>&1 && \
196+
systemctl is-system-running --quiet 2>/dev/null; then
197+
systemctl daemon-reload || true
198+
fi
199+
echo "LightNVR installed."
200+
echo " Start: sudo systemctl start lightnvr"
201+
echo " Enable boot: sudo systemctl enable lightnvr"
202+
echo " Config: /etc/lightnvr/lightnvr.ini"
203+
echo " Web UI: http://localhost:8080"
204+
POSTINST_EOF
205+
chmod 755 "${PKG_DIR}/DEBIAN/postinst"
206+
207+
# ── DEBIAN/prerm ────────────────────────────────────────────────────
208+
cat > "${PKG_DIR}/DEBIAN/prerm" << 'PRERM_EOF'
209+
#!/bin/sh
210+
set -e
211+
if command -v systemctl >/dev/null 2>&1; then
212+
systemctl stop lightnvr 2>/dev/null || true
213+
systemctl disable lightnvr 2>/dev/null || true
214+
fi
215+
PRERM_EOF
216+
chmod 755 "${PKG_DIR}/DEBIAN/prerm"
217+
218+
# ── DEBIAN/postrm ───────────────────────────────────────────────────
219+
cat > "${PKG_DIR}/DEBIAN/postrm" << 'POSTRM_EOF'
220+
#!/bin/sh
221+
set -e
222+
if [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
223+
ldconfig
224+
fi
225+
POSTRM_EOF
226+
chmod 755 "${PKG_DIR}/DEBIAN/postrm"
227+
228+
# ── Permissions ─────────────────────────────────────────────────────
229+
chmod 755 "${PKG_DIR}/usr/bin/lightnvr"
230+
chmod 755 "${PKG_DIR}/usr/bin/go2rtc"
231+
find "${PKG_DIR}/usr/lib/lightnvr" -type f -exec chmod 644 {} \;
232+
find "${PKG_DIR}/usr/lib/lightnvr" -type l | while read link; do
233+
chmod -h 777 "$link" 2>/dev/null || true
234+
done
235+
236+
# ── Build .deb ──────────────────────────────────────────────────────
237+
dpkg-deb --build --root-owner-group "${PKG_DIR}" "pkg/${PKG_NAME}.deb"
238+
echo "Built: pkg/${PKG_NAME}.deb ($(du -sh "pkg/${PKG_NAME}.deb" | cut -f1))"
239+
240+
- name: Upload .deb artifact
241+
uses: actions/upload-artifact@v6
242+
with:
243+
name: deb-${{ matrix.deb_arch }}
244+
path: pkg/*.deb
245+
retention-days: 7
246+
247+
upload-to-release:
248+
name: Upload .deb packages to GitHub Release
249+
needs: build-deb
250+
runs-on: ubuntu-latest
251+
if: startsWith(github.ref, 'refs/tags/')
252+
253+
steps:
254+
- name: Download all .deb artifacts
255+
uses: actions/download-artifact@v5
256+
with:
257+
pattern: deb-*
258+
merge-multiple: true
259+
path: packages/
260+
261+
- name: List packages
262+
run: ls -lh packages/
263+
264+
- name: Upload to GitHub Release
265+
uses: softprops/action-gh-release@v2
266+
with:
267+
tag_name: ${{ github.ref_name }}
268+
files: packages/*.deb
269+
fail_on_unmatched_files: true
270+
env:
271+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
272+

0 commit comments

Comments
 (0)