ci: add an arch tarball with static opencv

This commit is contained in:
Felitendo committed 2026-09-28 10:21:39 +02:00
1 parent 0a48ba9338
commit fd8449ea31
4 files changed
+209 -13

No files matched your search

+41 -3
View File
@@ -127,9 +127,47 @@ jobs:
dist/rpm-signer.asc dist/rpm-signer.asc
if-no-files-found: error if-no-files-found: error
tarball:
name: Arch Linux tarball
runs-on: ubuntu-latest
# Built on Arch itself, because the agent only fits the Qt it was built
# against. OpenCV is linked in, so no opencv here.
container: archlinux:latest
steps:
- name: Install the build tools
run: |
pacman -Syu --noconfirm --needed git base-devel cmake pkgconf curl zstd \
qt6-base qt6-declarative qt6-wayland layer-shell-qt ki18n \
pam systemd-libs gettext scdoc
- uses: actions/checkout@v7
- name: Check the tag against the Makefile
run: packaging/check-version.sh "${{ github.ref_name }}"
- run: packaging/build-opencv.sh
- run: packaging/build-tarball.sh
- name: Install it and run it
run: |
tar -xf dist/*.tar.zst --strip-components=1 -C /
for f in /usr/lib/face-unlock/* /usr/lib/security/pam_face_unlock.so; do
if ldd "$f" | grep 'not found'; then echo "$f is missing a library"; exit 1; fi
done
/usr/lib/face-unlock/face-unlockd --version
useradd -m tester
runuser -u tester -- face-unlock --version
- uses: actions/upload-artifact@v7
with:
name: tarball
path: dist/*.tar.zst
if-no-files-found: error
publish: publish:
name: Release and repositories name: Release and repositories
needs: [deb, rpm] needs: [deb, rpm, tarball]
if: startsWith(github.ref, 'refs/tags/v') || inputs.dry_run if: startsWith(github.ref, 'refs/tags/v') || inputs.dry_run
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -152,9 +190,9 @@ jobs:
gh release create "${{ github.ref_name }}" \ gh release create "${{ github.ref_name }}" \
--title "$(.github/release-notes.sh --title "${{ github.ref_name }}")" \ --title "$(.github/release-notes.sh --title "${{ github.ref_name }}")" \
--notes-file notes.md \ --notes-file notes.md \
incoming/*.deb incoming/*.rpm \ incoming/*.deb incoming/*.rpm incoming/*.tar.zst \
|| gh release upload "${{ github.ref_name }}" \ || gh release upload "${{ github.ref_name }}" \
incoming/*.deb incoming/*.rpm --clobber incoming/*.deb incoming/*.rpm incoming/*.tar.zst --clobber
- name: Install the repository tools - name: Install the repository tools
run: | run: |
+21 -10
View File
@@ -8,13 +8,15 @@ description of the layout, and two descriptions drift.
|---|---| |---|---|
| `deb/control`, `deb/copyright` | metadata for the Debian binary package | | `deb/control`, `deb/copyright` | metadata for the Debian binary package |
| `rpm/face-unlock.spec` | the RPM spec | | `rpm/face-unlock.spec` | the RPM spec |
| `build-deb.sh`, `build-rpm.sh` | build one package into `dist/` | | `build-deb.sh`, `build-rpm.sh`, `build-tarball.sh` | build one package into `dist/` |
| `build-opencv.sh` | builds the static OpenCV the Arch tarball links in |
| `check-version.sh` | refuses a tag that disagrees with the Makefile | | `check-version.sh` | refuses a tag that disagrees with the Makefile |
| `publish-repos.sh` | regenerates the APT and RPM repositories | | `publish-repos.sh` | regenerates the APT and RPM repositories |
| `pages/` | the landing page and the `.repo` file served from GitHub Pages | | `pages/` | the landing page and the `.repo` file served from GitHub Pages |
The AUR package lives in [Felitendo/PKGBUILDS](https://github.com/Felitendo/PKGBUILDS/tree/main/face-unlock). The AUR packages live in [Felitendo/PKGBUILDS](https://github.com/Felitendo/PKGBUILDS):
Its CI notices a new GitHub release, updates the checksum and pushes to the AUR. `face-unlock` builds from source, `face-unlock-bin` takes the Arch tarball.
Its CI notices a new GitHub release, updates the checksums and pushes to the AUR.
Unlike the shell-only LoonixTools, this one is compiled. The packages are per Unlike the shell-only LoonixTools, this one is compiled. The packages are per
architecture (amd64 and x86_64), and they need the Qt 6 and KDE Frameworks 6 architecture (amd64 and x86_64), and they need the Qt 6 and KDE Frameworks 6
@@ -28,6 +30,13 @@ against. The `.deb` is therefore built twice, in Debian 13 and in Ubuntu 26.04
gets an APT repository of its own: `deb/trixie` and `deb/resolute`. The RPM is gets an APT repository of its own: `deb/trixie` and `deb/resolute`. The RPM is
built on the current Fedora. built on the current Fedora.
The Arch tarball (`face-unlock-<version>-arch-x86_64.tar.zst`) is built on Arch,
for the same reason. It holds a folder with the `make install` tree. Arch
changes the OpenCV soname with every new OpenCV, so OpenCV is linked in
statically (`build-opencv.sh`: only the modules the daemon uses, nothing it
would load at run time). Qt stays shared, so a new Qt minor version on Arch
needs a new release.
The two networks (YuNet and SFace, from the OpenCV model zoo) are not in the The two networks (YuNet and SFace, from the OpenCV model zoo) are not in the
repository. `make models` downloads them and checks them against the repository. `make models` downloads them and checks them against the
checksums in the Makefile. The RPM spec and the PKGBUILD list them as sources checksums in the Makefile. The RPM spec and the PKGBUILD list them as sources
@@ -44,9 +53,10 @@ disables the socket.
```bash ```bash
packaging/build-deb.sh # in a Debian 13 container, with the -dev packages from release.yml packaging/build-deb.sh # in a Debian 13 container, with the -dev packages from release.yml
packaging/build-rpm.sh # in a Fedora container, with the -devel packages from the spec packaging/build-rpm.sh # in a Fedora container, with the -devel packages from the spec
packaging/build-opencv.sh && packaging/build-tarball.sh # in an Arch container, with the packages from release.yml
``` ```
Both take the version from `make version` unless one is passed as the first All three take the version from `make version` unless one is passed as the first
argument. argument.
## Making a release ## Making a release
@@ -56,10 +66,11 @@ argument.
2. Add the release to `CHANGELOG.md`, in the format CLAUDE.md describes. 2. Add the release to `CHANGELOG.md`, in the format CLAUDE.md describes.
3. Commit, then `git tag vX.Y.Z && git push --tags`. 3. Commit, then `git tag vX.Y.Z && git push --tags`.
The `release` workflow builds both packages in a Debian and a Fedora container, The `release` workflow builds the packages in Debian, Ubuntu, Fedora and Arch
refuses the tag if it disagrees with the Makefile or has no changelog entry, containers, refuses the tag if it disagrees with the Makefile or has no
attaches the packages to a GitHub release with the entry as its notes, and adds changelog entry, attaches the packages to a GitHub release with the entry as
them to the APT and RPM repositories on the `gh-pages` branch. its notes, and adds the deb and rpm files to the APT and RPM repositories on
the `gh-pages` branch.
## Trying the release path first ## Trying the release path first
@@ -67,7 +78,7 @@ them to the APT and RPM repositories on the `gh-pages` branch.
gh workflow run release.yml -f dry_run=true gh workflow run release.yml -f dry_run=true
``` ```
Builds both packages, builds both repositories with a key generated on the Builds the packages, builds both repositories with a key generated on the
spot, checks the signatures, and installs the packages back out of the spot, checks the signatures, and installs the packages back out of the
repositories. Nothing is pushed and no release is made. repositories. Nothing is pushed and no release is made.
@@ -81,7 +92,7 @@ gpg --armor --export-secret-keys 'face-unlock repository' \
| gh secret set GPG_PRIVATE_KEY | gh secret set GPG_PRIVATE_KEY
``` ```
Without the secret the workflow still builds both packages and attaches them to Without the secret the workflow still builds the packages and attaches them to
the release; it says so in the log and leaves the repositories alone. the release; it says so in the log and leaves the repositories alone.
## Pointing Pages at it, once, in this order ## Pointing Pages at it, once, in this order
+77
View File
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
#
# Builds the OpenCV the Arch tarball links in, as static libraries.
#
# Arch moves to a new OpenCV now and then, each with a new soname, and a
# daemon linked against the old one then no longer starts. Linked in
# statically, the daemon brings its own. Only the modules face-unlock uses are
# built, with OpenCV's own copies of zlib, libjpeg and libpng, so nothing is
# left to load at run time. The camera is read through V4L2, which OpenCV
# talks to directly.
#
# packaging/build-opencv.sh [PREFIX]
#
# Installs into PREFIX (build/opencv-static by default) and does nothing when
# PREFIX already holds what this script builds. Needs: cmake, a C++ compiler,
# curl.
set -euo pipefail
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
prefix="${1:-$here/build/opencv-static}"
# 4.x, like the deb and the rpm. The static dnn of 5.0.0 does not link
# (https://github.com/opencv/opencv/issues/29342).
version=4.14.0
sha256=ee8fb9b30eb60850431b4656447080e3737b56e45719c92b67f245950609f86e
# A change to this script is a different build.
stamp="$(sha256sum "${BASH_SOURCE[0]}" | cut -d' ' -f1)"
if [[ -f $prefix/.stamp && $(<"$prefix/.stamp") == "$stamp" ]]; then
echo "$prefix already has this OpenCV"
exit 0
fi
# It is emptied before the install, so it has to be one of ours.
if [[ -e $prefix && ! -f $prefix/.stamp ]]; then
echo "$0: $prefix exists and was not made by this script" >&2
exit 1
fi
work="$(mktemp -d)"
trap 'rm -rf -- "$work"' EXIT
curl -fL --retry 3 -o "$work/opencv.tar.gz" \
"https://github.com/opencv/opencv/archive/refs/tags/$version.tar.gz"
echo "$sha256 $work/opencv.tar.gz" | sha256sum -c --quiet -
tar -xzf "$work/opencv.tar.gz" -C "$work"
src="$work/opencv-$version"
# BUILD_LIST adds what the listed modules need (calib3d, features2d, flann).
# The rest is switched off because it would be picked up from the system or
# downloaded: codecs, video backends, IPP and the other accelerators.
cmake -S "$src" -B "$work/build" \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX="$prefix" \
-DBUILD_SHARED_LIBS=OFF \
-DBUILD_LIST=core,imgproc,imgcodecs,videoio,objdetect,dnn \
-DBUILD_TESTS=OFF -DBUILD_PERF_TESTS=OFF -DBUILD_EXAMPLES=OFF \
-DBUILD_DOCS=OFF -DBUILD_opencv_apps=OFF -DBUILD_JAVA=OFF \
-DBUILD_ZLIB=ON -DBUILD_JPEG=ON -DBUILD_PNG=ON -DBUILD_PROTOBUF=ON \
-DWITH_V4L=ON \
-DWITH_FFMPEG=OFF -DWITH_GSTREAMER=OFF -DWITH_OBSENSOR=OFF \
-DWITH_TIFF=OFF -DWITH_WEBP=OFF -DWITH_AVIF=OFF -DWITH_OPENEXR=OFF \
-DWITH_OPENJPEG=OFF -DWITH_JASPER=OFF \
-DWITH_IPP=OFF -DWITH_ITT=OFF -DWITH_OPENCL=OFF -DWITH_VA=OFF -DWITH_VA_INTEL=OFF \
-DWITH_LAPACK=OFF -DWITH_EIGEN=OFF -DWITH_FLATBUFFERS=OFF -DWITH_QUIRC=OFF -DWITH_ADE=OFF
cmake --build "$work/build" --parallel "$(nproc)"
rm -rf -- "$prefix"
cmake --install "$work/build"
# OpenCV installs the licences of the libraries in it, but not its own.
mv "$prefix"/share/licenses/opencv* "$prefix/share/licenses/opencv"
install -Dm644 "$src/LICENSE" "$prefix/share/licenses/opencv/LICENSE"
# A picture with a face, for the check that the models load and find one.
install -Dm644 "$src/samples/data/messi5.jpg" "$prefix/share/face-unlock-check/face.jpg"
echo "$stamp" > "$prefix/.stamp"
+70
View File
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
#
# Builds the tarball for Arch Linux into dist/. The AUR package
# face-unlock-bin is made from it.
#
# Like the deb and the rpm, it holds what `make install` produced, under a
# folder named like the tarball. One thing is different: OpenCV is linked in
# statically (packaging/build-opencv.sh), so a new OpenCV on Arch does not
# break the daemon. Qt stays shared. The agent uses Qt's private API, so the
# tarball fits the Qt that Arch had when it was built.
#
# Needs: make, cmake, a C++ compiler, the packages check.yml installs (without
# opencv), msgfmt (gettext), scdoc, curl, zstd, and the static OpenCV:
#
# packaging/build-opencv.sh && packaging/build-tarball.sh
set -euo pipefail
here="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
version="${1:-$(make -s -C "$here" version)}"
opencv="${OPENCV_PREFIX:-$here/build/opencv-static}"
name="face-unlock-$version-arch-$(uname -m)"
for tool in msgfmt scdoc cmake readelf zstd; do
command -v "$tool" > /dev/null || { echo "$0: $tool is not installed" >&2; exit 1; }
done
opencv_dir="$(dirname "$(find "$opencv" -name OpenCVConfig.cmake -print -quit 2>/dev/null)")"
[[ $opencv_dir != . ]] || { echo "$0: no OpenCV in $opencv, run packaging/build-opencv.sh first" >&2; exit 1; }
root="$(mktemp -d)"
work="$(mktemp -d)"
trap 'rm -rf -- "$root" "$work"' EXIT
dest="$root/$name"
make -C "$here" models
make -C "$here" install \
DESTDIR="$dest" \
PREFIX=/usr \
VERSION="$version" \
BUILDDIR="$work/build" \
PAMDIR=/usr/lib/security \
SYSTEMUNITDIR=/usr/lib/systemd/system \
USERUNITDIR=/usr/lib/systemd/user \
CMAKE_FLAGS="-DOpenCV_DIR=$opencv_dir"
# The tests, and the models on a real face: the part a smaller OpenCV could
# break without anything else noticing.
ctest --test-dir "$work/build" --output-on-failure
face="$opencv/share/face-unlock-check/face.jpg"
"$work/build/test_images" "$dest/usr/share/face-unlock/models" "$face" "$face" | tee "$work/faces"
grep -q 'similarity' "$work/faces" || { echo "$0: the models found no face" >&2; exit 1; }
if readelf -d "$dest/usr/lib/face-unlock/face-unlockd" | grep -q 'libopencv'; then
echo "$0: the daemon still loads a shared OpenCV" >&2
exit 1
fi
# The program is GPL, OpenCV and the libraries in it come with their own
# licences, and the copyright file names the models' authors and licences.
lic="$dest/usr/share/licenses/face-unlock"
install -Dm644 "$here/LICENSE" "$lic/LICENSE"
install -Dm644 "$here/packaging/deb/copyright" "$lic/copyright"
cp -r "$opencv/share/licenses/opencv" "$lic/opencv"
mkdir -p "$here/dist"
out="$here/dist/$name.tar.zst"
tar -C "$root" --owner=0 --group=0 --numeric-owner --sort=name \
-I 'zstd -19 -T0' -cf "$out" "$name"
echo "$out"