diff --git a/.copr/Makefile b/.copr/Makefile index 723237b..b335e8c 100644 --- a/.copr/Makefile +++ b/.copr/Makefile @@ -1,4 +1,4 @@ -VERSION := $(shell git describe --tags --exact-match 2>/dev/null | sed 's/^v//' | grep . || echo 0.1.1) +VERSION := $(shell git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//; s/-/~/g' | grep .) SPEC := secureEye/rpm/secure-eye.spec outdir ?= $(CURDIR) topdir := $(CURDIR)/.copr-rpmbuild @@ -7,11 +7,14 @@ topdir := $(CURDIR)/.copr-rpmbuild srpm: dnf -y install git rpm-build git config --global --add safe.directory $(CURDIR) + scripts/set-package-version.sh "$(VERSION)" + git -c user.name=copr -c user.email=copr@localhost \ + commit -qam "generated packaging for $(VERSION)" --allow-empty mkdir -p "$(topdir)/SOURCES" "$(outdir)" git archive --format=tar.gz \ --prefix="secure-eye-$(VERSION)/" \ -o "$(topdir)/SOURCES/secure-eye-$(VERSION).tar.gz" HEAD - cp secureEye/rpm/secureeye-authd.sysusers "$(topdir)/SOURCES/" + cp secureEye/rpm/secure-eye.sysusers "$(topdir)/SOURCES/" rpmbuild -bs $(SPEC) \ --define "_topdir $(topdir)" \ --define "pkg_version $(VERSION)" \ diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index b451261..69f3a99 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -1,5 +1,9 @@ name: check -on: [push, pull_request] +on: + push: + branches: [ main, dev ] + pull_request: + branches: [ main, dev ] jobs: test: @@ -7,11 +11,11 @@ jobs: steps: - name: Install required libraries run: > - sudo apt-get update && sudo apt-get install -y - python3 python3-pip python3-setuptools python3-wheel - cmake make build-essential clang-tidy - libpam0g-dev libinih-dev libevdev-dev - python3-dev libopencv-dev + sudo apt-get update && sudo apt-get install -y + --no-install-recommends + python3 python3-pip python3-dev + make build-essential clang-tidy pkg-config + libpam0g-dev libinih-dev libevdev-dev systemd-dev - name: Install meson run: sudo python3 -m pip install meson ninja diff --git a/.github/workflows/publish-ppa.yml b/.github/workflows/publish-ppa.yml deleted file mode 100644 index 57ddc0d..0000000 --- a/.github/workflows/publish-ppa.yml +++ /dev/null @@ -1,70 +0,0 @@ -# MIT License -# -# Copyright (c) 2021 Kevin Yue -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -name: Publish to Launchpad PPA - -on: - workflow_run: - workflows: [ "Build Debian packages" ] - types: [ completed ] - -permissions: - contents: read - -concurrency: - group: publish-ppa-${{ github.event.workflow_run.head_branch }} - cancel-in-progress: false - -jobs: - publish: - name: Publish source packages to PPA - # Only for release-triggered, successful runs of the deb build. - if: >- - github.event.workflow_run.event == 'release' && - github.event.workflow_run.conclusion == 'success' - runs-on: ubuntu-24.04 - - environment: - name: launchpad-ppa - url: https://launchpad.net/~vhrabar/+archive/ubuntu/tools - steps: - - name: Check out the released commit - uses: actions/checkout@v4 - with: - ref: ${{ github.event.workflow_run.head_sha }} - - - name: Publish to Launchpad PPA - run: bash scripts/publish-ppa.sh - env: - REPOSITORY: "vhrabar/tools" - GPG_PRIVATE_KEY: ${{ secrets.PPA_GPG_PRIVATE_KEY }} - GPG_PASSPHRASE: ${{ secrets.PPA_GPG_PASSPHRASE }} - DEBEMAIL: "vedran.hrabar@outlook.com" - DEBFULLNAME: "Vedran Hrabar" - - DEBIAN_DIR: "secureEye/debian" - SERIES: "questing resolute stonking" - - NEW_VERSION_TEMPLATE: "{VERSION}~{SERIES}{REVISION}" - - # GitHub's auto-generated source tarball for the released tag. - TARBALL: "https://github.com/${{ github.repository }}/archive/refs/tags/${{ github.event.workflow_run.head_branch }}.tar.gz" diff --git a/.github/workflows/release-debs.yml b/.github/workflows/release-debs.yml deleted file mode 100644 index 47de05f..0000000 --- a/.github/workflows/release-debs.yml +++ /dev/null @@ -1,166 +0,0 @@ -name: Build Debian packages - -# Builds binary .deb packages for amd64, amd64v3 (x86-64-v3) and arm64, uploads & attaches to release. -on: - release: - types: [ published ] - workflow_dispatch: - -permissions: - contents: write - -jobs: - build: - name: build (${{ matrix.name }}) - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - # Baseline amd64. - - name: amd64 - runner: ubuntu-24.04 - march: "" - version_suffix: "" - dpkg_flags: "-b" - vendor_mediapipe: "true" - smoke: "true" - # x86-64-v3 micro-architecture optimised amd64 build - - name: amd64v3 - runner: ubuntu-24.04 - march: "x86-64-v3" - version_suffix: "+v3" - dpkg_flags: "-B" - vendor_mediapipe: "true" - smoke: "false" - # arm64 (native runner). - - name: arm64 - runner: ubuntu-24.04-arm - march: "" - version_suffix: "" - dpkg_flags: "-B" - vendor_mediapipe: "false" - smoke: "true" - - steps: - - uses: actions/checkout@v4 - - - name: Install build dependencies - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - debhelper devscripts dpkg-dev build-essential fakeroot lintian \ - meson ninja-build pkg-config cmake gettext \ - distro-info-data libdistro-info-perl \ - python3 python3-pip python3-venv \ - libpam0g-dev libevdev-dev libinih-dev - - - name: Stage debian/ at the source root - run: | - cp -r secureEye/debian ./debian - rm -rf secureEye/debian - - - name: Set package version - env: - DEBEMAIL: "vedran.hrabar@outlook.com" - DEBFULLNAME: "Vedran Hrabar" - run: | - current="$(dpkg-parsechangelog -l debian/changelog -S Version)" - base="$current" - if [ "${{ github.event_name }}" = "release" ]; then - tag="${{ github.ref_name }}" - base="${tag#v}" - fi - if [ -z "$base" ]; then - echo "::error::Could not determine package version (empty)." - exit 1 - fi - version="${base}${{ matrix.version_suffix }}" - if [ "$version" != "$current" ]; then - dch -b -v "$version" -D unstable -u low \ - "Automated CI build (${{ matrix.name }})" - fi - echo "Building secure-eye ${version}" - # vendors mediapipe wheels if vendor_mediapipe is true. - - name: Vendor Python wheels - run: | - mkdir -p debian/wheels - if [ "${{ matrix.vendor_mediapipe }}" = "true" ]; then - python3 -m pip download \ - --only-binary=:all: --no-cache-dir --no-deps \ - --platform manylinux_2_28_x86_64 \ - --platform manylinux_2_17_x86_64 \ - --platform manylinux2014_x86_64 \ - --dest debian/wheels \ - -r requirements-vendor.txt - else - grep -iv '^[[:space:]]*mediapipe' requirements-vendor.txt > /tmp/reqs.txt - python3 -m pip download \ - --only-binary=:all: --no-cache-dir --no-deps \ - --dest debian/wheels \ - -r /tmp/reqs.txt - fi - ls -la debian/wheels - - - name: Build packages - run: | - if [ -n "${{ matrix.march }}" ]; then - export DEB_CFLAGS_APPEND="-march=${{ matrix.march }}" - export DEB_CXXFLAGS_APPEND="-march=${{ matrix.march }}" - fi - dpkg-buildpackage ${{ matrix.dpkg_flags }} -us -uc - - # dpkg-buildpackage writes the artifacts to the parent directory. - - name: Collect artifacts - run: | - mkdir -p dist - mv ../*.deb dist/ 2>/dev/null || true - mv ../*.ddeb dist/ 2>/dev/null || true - ls -la dist - - - name: Lintian - run: lintian --fail-on error --tag-display-limit 0 dist/*.deb - - - name: Install smoke test - if: matrix.smoke == 'true' - run: | - docker run --rm -v "$PWD/dist:/debs:ro" ubuntu:26.04 bash -euc ' - export DEBIAN_FRONTEND=noninteractive - apt-get update - apt-get install -y --no-install-recommends software-properties-common - add-apt-repository -y ppa:vhrabar/tools - # apt resolves the bundled .debs plus their archive/PPA dependencies. - apt-get install -y --no-install-recommends /debs/*.deb - # Confirm the daemon package configured cleanly. - dpkg -s secureeye-authd | grep -q "^Status: install ok installed" - dpkg -s libpam-secureeye | grep -q "^Status: install ok installed" - echo "Install smoke test passed." - ' - - - uses: actions/upload-artifact@v4 - with: - name: deb-${{ matrix.name }} - path: dist/* - if-no-files-found: error - - release: - name: Attach to release - needs: build - if: github.event_name == 'release' - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - with: - path: dist - pattern: deb-* - merge-multiple: true - - - name: List downloaded packages - run: ls -la dist - - - name: Upload to release - uses: softprops/action-gh-release@v2 - with: - files: | - dist/*.deb - dist/*.ddeb diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml new file mode 100644 index 0000000..adc567f --- /dev/null +++ b/.github/workflows/release-prep.yml @@ -0,0 +1,108 @@ +name: Prepare release + +# Automates the manual part of cutting a release: +# +# patch versions -> commit to the default branch -> tag it -> draft a release + +on: + workflow_dispatch: + inputs: + version: + description: "Version to release, e.g. 0.1.4 (no leading v)." + required: true + type: string + +permissions: + contents: write + +concurrency: + group: release-prep + cancel-in-progress: false + +jobs: + prepare: + name: Prepare ${{ inputs.version }} + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ github.event.repository.default_branch }} + fetch-depth: 0 + + - name: Validate the version and check the tag is free + env: + VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + case "$VERSION" in + [0-9]*) ;; + *) echo "::error::Version '$VERSION' must start with a digit."; exit 1 ;; + esac + # Same rule release.yml enforces: rpm and deb want 1.0.0~rc1, Arch's + # pkgver allows neither '-' nor '~'. + case "$VERSION" in + *-*) echo "::error::'$VERSION' is a pre-release. rpm/deb/Arch cannot represent '-' consistently; use a plain X.Y.Z."; exit 1 ;; + esac + if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then + echo "::error::Tag v${VERSION} already exists. Pick another version, or delete the tag first." + exit 1 + fi + echo "v${VERSION} is free." + + - name: Require notes under [Unreleased] + run: | + set -euo pipefail + if ! scripts/changelog-section.sh Unreleased >/dev/null 2>&1; then + echo "::error::CHANGELOG.md has no content under '## [Unreleased]'. Add the release notes first." + exit 1 + fi + echo "Notes found under [Unreleased]:" + scripts/changelog-section.sh Unreleased + + - name: Patch versions and date the changelog section + env: + VERSION: ${{ inputs.version }} + DEBEMAIL: "vedran.hrabar@outlook.com" + DEBFULLNAME: "Vedran Hrabar" + run: scripts/set-package-version.sh "$VERSION" + + - name: Commit and tag + id: commit + env: + VERSION: ${{ inputs.version }} + BRANCH: ${{ github.event.repository.default_branch }} + run: | + set -euo pipefail + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add -A + if git diff --cached --quiet; then + echo "::error::Nothing changed; is CHANGELOG.md already at ${VERSION}?" + exit 1 + fi + git commit -m "chore(release): ${VERSION}" + + # Push the commit before tagging so the tag lands on the branch HEAD. + git push origin "HEAD:${BRANCH}" + git tag -a "v${VERSION}" -m "SecureEye ${VERSION}" + git push origin "refs/tags/v${VERSION}" + echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" + + - name: Draft the release from CHANGELOG.md + env: + VERSION: ${{ inputs.version }} + GH_TOKEN: ${{ github.token }} + run: | + set -euo pipefail + scripts/changelog-section.sh "$VERSION" > /tmp/notes.md + gh release create "v${VERSION}" \ + --draft \ + --title "SecureEye ${VERSION}" \ + --notes-file /tmp/notes.md \ + --target "${{ steps.commit.outputs.sha }}" + url="$(gh release view "v${VERSION}" --json url --jq .url)" + { + echo "### Draft release for \`${VERSION}\` is ready" + echo "" + echo "Review it, then press **Publish** to start the release: $url" + } >> "$GITHUB_STEP_SUMMARY" diff --git a/.github/workflows/release-rpms.yml b/.github/workflows/release-rpms.yml deleted file mode 100644 index 9954261..0000000 --- a/.github/workflows/release-rpms.yml +++ /dev/null @@ -1,110 +0,0 @@ - -name: Build RPM packages - -# Builds binary .rpm packages for x86_64, x86_64 (x86-64-v3) and aarch64, -# uploads & attaches to release. -on: - release: - types: [ published ] - workflow_dispatch: - -permissions: - contents: write - -env: - FEDORA_IMAGE: fedora:44 - -jobs: - build: - name: build (${{ matrix.name }}) - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - # Baseline x86_64. - - name: x86_64 - runner: ubuntu-24.04 - march: "" - rel_suffix: "" - smoke: "true" - # x86-64-v3 micro-architecture optimised build. - - name: x86_64_v3 - runner: ubuntu-24.04 - march: "x86-64-v3" - rel_suffix: ".v3" - smoke: "false" - # aarch64 (native runner). - - name: aarch64 - runner: ubuntu-24.04-arm - march: "" - rel_suffix: "" - smoke: "true" - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Determine package version - id: ver - run: | - version="0.1.1" - if [ "${{ github.event_name }}" = "release" ]; then - tag="${{ github.ref_name }}" - version="${tag#v}" - fi - if [ -z "$version" ]; then - echo "::error::Could not determine package version (empty)." - exit 1 - fi - echo "version=$version" >> "$GITHUB_OUTPUT" - echo "Building secure-eye $version (${{ matrix.name }})" - - - name: Build packages (Fedora container) - run: | - docker run --rm -v "$PWD:/src" -w /src "$FEDORA_IMAGE" \ - env PKG_VERSION="${{ steps.ver.outputs.version }}" \ - MARCH="${{ matrix.march }}" \ - REL_SUFFIX="${{ matrix.rel_suffix }}" \ - bash scripts/build-rpm.sh - - - name: Install smoke test - if: matrix.smoke == 'true' - run: | - docker run --rm -v "$PWD/dist:/rpms:ro" "$FEDORA_IMAGE" bash -euc ' - dnf install -y /rpms/*.rpm - rpm -q libpam-secureeye secureeye-authd secure-eye - # %post must have built the recognition venv offline from the wheels. - test -x /usr/lib/secureeye-authd/venv/bin/python3 - id secureeye - test -f /usr/lib/systemd/system/secureeye-authd.service - test -f /usr/lib64/security/pam_secureEye.so - echo "Install smoke test passed." - ' - - - uses: actions/upload-artifact@v4 - with: - name: rpm-${{ matrix.name }} - path: dist/*.rpm - if-no-files-found: error - - release: - name: Attach to release - needs: build - if: github.event_name == 'release' - runs-on: ubuntu-latest - steps: - - uses: actions/download-artifact@v4 - with: - path: dist - pattern: rpm-* - merge-multiple: true - - - name: List downloaded packages - run: ls -la dist - - - name: Upload to release - uses: softprops/action-gh-release@v2 - with: - files: dist/*.rpm diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..63ca622 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,459 @@ +name: Release + +# Single entry point for a release: +# +# test -> verify deb / rpm / arch build and install -> publish +# +on: + release: + types: [ published ] + workflow_dispatch: + inputs: + version: + description: "Version to build (defaults to the latest tag). No leading v." + required: false + type: string + publish: + description: "Also publish to Launchpad, COPR and the AUR." + required: false + default: false + type: boolean + +permissions: + contents: read + +concurrency: + group: release-${{ github.event.release.tag_name || github.ref }} + cancel-in-progress: false + +env: + FEDORA_IMAGE: fedora:44 + ARCH_IMAGE: archlinux:base-devel + UBUNTU_IMAGE: ubuntu:26.04 + +jobs: + version: + name: Resolve version + runs-on: ubuntu-24.04 + outputs: + version: ${{ steps.ver.outputs.version }} + tag: ${{ steps.ver.outputs.tag }} + publish: ${{ steps.ver.outputs.publish }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Determine version + id: ver + env: + RELEASE_TAG: ${{ github.event.release.tag_name }} + INPUT_VERSION: ${{ inputs.version }} + run: | + set -euo pipefail + if [ "${{ github.event_name }}" = "release" ]; then + tag="$RELEASE_TAG" + version="${tag#v}" + publish=true + else + version="${INPUT_VERSION:-$(git describe --tags --abbrev=0 | sed 's/^v//')}" + tag="v${version}" + publish="${{ inputs.publish }}" + fi + case "$version" in + [0-9]*) ;; + *) echo "::error::Refusing to build version '$version' (must start with a digit)."; exit 1 ;; + esac + # A pre-release tag cannot be spelled the same way in all three + # formats: rpm and deb want 1.0.0~rc1, Arch's pkgver allows neither + # '-' nor '~', and the PKGBUILD derives its source URL from pkgver. + # Rather than publish three versions that sort differently, refuse. + case "$version" in + *-*) echo "::error::Pre-release tag '$tag' is not publishable: rpm/deb/Arch cannot represent '-' consistently. Tag a plain X.Y.Z release."; exit 1 ;; + esac + if [ "$publish" = "true" ]; then + branch="${{ github.event.repository.default_branch }}" + + git fetch --no-tags origin "+refs/heads/${branch}:refs/remotes/origin/${branch}" + git fetch origin "+refs/tags/${tag}:refs/tags/${tag}" + tag_sha="$(git rev-parse "refs/tags/${tag}^{commit}")" + head_sha="$(git rev-parse "refs/remotes/origin/${branch}")" + if [ "$tag_sha" != "$head_sha" ]; then + echo "::error::${tag} is at ${tag_sha}, but ${branch} is at ${head_sha}. The tag must be on ${branch} HEAD, otherwise COPR builds a different commit than the PPA and AUR. Move the tag to HEAD, or reset ${branch} to the tag, then re-run." + exit 1 + fi + echo "$tag is on $branch HEAD ($tag_sha)" + fi + + { + echo "version=$version" + echo "tag=$tag" + echo "publish=$publish" + } >> "$GITHUB_OUTPUT" + echo "### secure-eye \`$version\` (tag \`$tag\`, publish: \`$publish\`)" >> "$GITHUB_STEP_SUMMARY" + + test: + name: Tests + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + + - name: Set up Python + run: uv python install 3.12 + + - name: Install dependencies + run: uv sync + + - name: Ruff + run: uv run --no-sync ruff check . + + - name: Black + run: uv run --no-sync black --check . + + - name: Pytest + run: uv run pytest tests/ -v + + verify-deb: + name: Verify deb (${{ matrix.name }}) + needs: [ version, test ] + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - name: amd64 + runner: ubuntu-24.04 + - name: arm64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + + - name: Install build dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + debhelper devscripts dpkg-dev build-essential fakeroot lintian \ + meson ninja-build pkg-config \ + libpam0g-dev libevdev-dev libinih-dev python3 + + - name: Set packaging version to ${{ needs.version.outputs.version }} + run: scripts/set-package-version.sh "${{ needs.version.outputs.version }}" + + - name: Stage debian/ at the source root + run: | + cp -r secureEye/debian ./debian + rm -rf secureEye/debian + + - name: Build packages + run: dpkg-buildpackage -b -us -uc + + # dpkg-buildpackage writes the artifacts to the parent directory. + - name: Collect artifacts + run: | + mkdir -p dist + mv ../*.deb ../*.ddeb dist/ 2>/dev/null || true + ls -la dist + + - name: Lintian + run: lintian --fail-on error --tag-display-limit 0 dist/*.deb + + - name: Install smoke test + run: | + docker run --rm -v "$PWD/dist:/debs:ro" "$UBUNTU_IMAGE" bash -euc ' + export DEBIAN_FRONTEND=noninteractive + apt-get update + apt-get install -y --no-install-recommends software-properties-common + # Recognition backends (python3-mediapipe, python3-dlib) live here. + add-apt-repository -y ppa:vhrabar/tools + apt-get install -y --no-install-recommends /debs/secure-eye_*.deb + dpkg -s secure-eye | grep -q "^Status: install ok installed" + # The split packages were merged away; nothing must resurrect them. + dpkg -s libpam-secureeye 2>/dev/null && { echo "libpam-secureeye reappeared"; exit 1; } + test -f /usr/lib/systemd/system/secureeye-authd.service + ls /usr/lib/*/security/pam_secureEye.so + test -x /usr/bin/secureEye + test -f /etc/secureEye/config.ini + echo "deb install smoke test passed." + ' + + - uses: actions/upload-artifact@v4 + with: + name: deb-${{ matrix.name }} + path: dist/* + if-no-files-found: error + retention-days: 7 + + verify-rpm: + name: Verify rpm (${{ matrix.name }}) + needs: [ version, test ] + runs-on: ${{ matrix.runner }} + strategy: + fail-fast: false + matrix: + include: + - name: x86_64 + runner: ubuntu-24.04 + - name: aarch64 + runner: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + # build-rpm.sh generates the changelogs from CHANGELOG.md and commits them + # locally itself, the same way COPR's .copr/Makefile does. + - name: Build packages (Fedora container) + run: | + docker run --rm -v "$PWD:/src" -w /src "$FEDORA_IMAGE" \ + env PKG_VERSION="${{ needs.version.outputs.version }}" \ + MARCH="" REL_SUFFIX="" \ + bash scripts/build-rpm.sh + + - name: Install smoke test + run: | + docker run --rm -v "$PWD/dist:/rpms:ro" "$FEDORA_IMAGE" bash -euc ' + dnf_opts="--setopt=timeout=180 --setopt=retries=10 --setopt=minrate=0" + dnf install -y $dnf_opts dnf-plugins-core + # Recognition backends (python3-mediapipe on x86_64, python3-dlib). + dnf copr enable -y vhrabar/python-extras + dnf install -y $dnf_opts /rpms/*.rpm + rpm -q secure-eye + rpm -q libpam-secureeye && { echo "libpam-secureeye reappeared"; exit 1; } + id secureeye + test -f /usr/lib/systemd/system/secureeye-authd.service + ls /usr/lib64/security/pam_secureEye.so + test -x /usr/bin/secureEye + test -f /etc/secureEye/config.ini + echo "rpm install smoke test passed." + ' + + - uses: actions/upload-artifact@v4 + with: + name: rpm-${{ matrix.name }} + path: dist/*.rpm + if-no-files-found: error + retention-days: 7 + + verify-arch: + name: Verify arch pkg (x86_64) + needs: [ version, test ] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Stage a build tree + env: + VERSION: ${{ needs.version.outputs.version }} + run: | + set -euo pipefail + mkdir -p archbuild + cp secureEye/archlinux/secureEye/PKGBUILD \ + secureEye/archlinux/secureEye/secure-eye.install \ + secureEye/archlinux/secureEye/.SRCINFO archbuild/ + git archive --format=tar.gz --prefix="SecureEye-${VERSION}/" \ + -o "archbuild/secure-eye-${VERSION}.tar.gz" HEAD + + - name: Build and install (Arch container) + env: + VERSION: ${{ needs.version.outputs.version }} + run: | + docker run --rm -v "$PWD:/src" -w /src -e VERSION "$ARCH_IMAGE" bash -euxc ' + pacman -Syu --noconfirm --needed \ + base-devel meson ninja pkgconf pam libevdev libinih git pacman-contrib + useradd -m builder + chown -R builder /src/archbuild + su builder -c " + set -eux + cd /src/archbuild + sed -i -E \"s/^pkgver=.*/pkgver=$VERSION/;s/^pkgrel=.*/pkgrel=1/\" PKGBUILD + updpkgsums + makepkg --nodeps --noconfirm + makepkg --printsrcinfo > .SRCINFO.generated + " + pacman -U --noconfirm -dd "/src/archbuild/secure-eye-$VERSION-1-"*.pkg.tar.zst + pacman -Qi secure-eye + test -f /usr/lib/systemd/system/secureeye-authd.service + ls /usr/lib/security/pam_secureEye.so + test -x /usr/bin/secureEye + test -f /etc/secureEye/config.ini + test -f /usr/lib/sysusers.d/secure-eye.conf + echo "arch install smoke test passed." + ' + + - name: Check .SRCINFO is in sync with PKGBUILD + run: | + filter() { grep -vE '^\s*(pkgver|sha256sums|source) =' "$1"; } + if ! diff -u <(filter archbuild/.SRCINFO) <(filter archbuild/.SRCINFO.generated); then + echo "::error::.SRCINFO is stale — run 'makepkg --printsrcinfo > .SRCINFO' in secureEye/archlinux/secureEye/" + exit 1 + fi + echo ".SRCINFO is in sync." + + - uses: actions/upload-artifact@v4 + with: + name: arch-x86_64 + path: archbuild/*.pkg.tar.zst + if-no-files-found: error + retention-days: 7 + + publish-ppa: + name: Publish to Launchpad PPA + needs: [ version, test, verify-deb, verify-rpm, verify-arch ] + if: needs.version.outputs.publish == 'true' + runs-on: ubuntu-24.04 + environment: + name: launchpad-ppa + url: https://launchpad.net/~vhrabar/+archive/ubuntu/tools + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.tag }} + + # debian/changelog is generated from CHANGELOG.md, not stored, so render + # it here. Deterministic, so this matches what CI verified and what COPR + # produces for the same commit. + - name: Generate packaging for ${{ needs.version.outputs.version }} + run: scripts/set-package-version.sh "${{ needs.version.outputs.version }}" + + - name: Publish to Launchpad PPA + run: bash scripts/publish-ppa.sh + env: + REPOSITORY: "vhrabar/tools" + GPG_PRIVATE_KEY: ${{ secrets.PPA_GPG_PRIVATE_KEY }} + GPG_PASSPHRASE: ${{ secrets.PPA_GPG_PASSPHRASE }} + DEBEMAIL: "vedran.hrabar@outlook.com" + DEBFULLNAME: "Vedran Hrabar" + + DEBIAN_DIR: "secureEye/debian" + SERIES: "questing resolute stonking" + + NEW_VERSION_TEMPLATE: "{VERSION}~{SERIES}{REVISION}" + + # The tag's tarball. debian/ is rendered from CHANGELOG.md above and + # handed to the build via DEBIAN_DIR, so it need not be in the tarball. + TARBALL: "https://github.com/${{ github.repository }}/archive/refs/tags/${{ needs.version.outputs.tag }}.tar.gz" + + publish-copr: + name: Build on COPR + needs: [ version, test, verify-deb, verify-rpm, verify-arch ] + if: needs.version.outputs.publish == 'true' + runs-on: ubuntu-24.04 + container: fedora:44 + environment: + name: copr + url: https://copr.fedorainfracloud.org/coprs/vhrabar/SecureEye/ + steps: + - name: Install copr-cli + run: dnf install -y --setopt=install_weak_deps=False copr-cli + + # An API build pinned to the tag, rather than the custom webhook. The + # webhook rebuilds the package from its stored SCM config, whose + # committish is unset, so COPR would clone the default branch's HEAD and + # could build a different commit than the PPA and the AUR. + - name: Submit an SCM build for ${{ needs.version.outputs.tag }} + env: + COPR_CONFIG_CONTENT: ${{ secrets.COPR_CONFIG }} + TAG: ${{ needs.version.outputs.tag }} + run: | + set -euo pipefail + if [ -z "${COPR_CONFIG_CONTENT:-}" ]; then + echo "::error::COPR_CONFIG secret is not set. Copy the token file from https://copr.fedorainfracloud.org/api/ into it." + exit 1 + fi + mkdir -p ~/.config + printf '%s\n' "$COPR_CONFIG_CONTENT" > ~/.config/copr + chmod 600 ~/.config/copr + grep -q '^\[copr-cli\]' ~/.config/copr \ + || { echo "::error::COPR_CONFIG does not look like a copr-cli token file (no [copr-cli] section)."; exit 1; } + + # --method make_srpm runs .copr/Makefile, which regenerates the + # changelogs from CHANGELOG.md for this exact commit. + copr-cli buildscm \ + --clone-url "https://github.com/${{ github.repository }}" \ + --commit "$TAG" \ + --method make_srpm \ + --nowait \ + vhrabar/SecureEye + + + publish-aur: + name: Publish to the AUR + needs: [ version, test, verify-deb, verify-rpm, verify-arch ] + if: needs.version.outputs.publish == 'true' + runs-on: ubuntu-24.04 + environment: + name: aur + url: https://aur.archlinux.org/packages/secure-eye + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.version.outputs.tag }} + + # The tag is never moved, so archive/refs/tags/.tar.gz is stable and + # updpkgsums hashes exactly what users download. These sums are valid only + # in the AUR copy: in-tree they would have to hash an archive containing + # themselves, which has no fixed point. + - name: Set pkgver and checksum the released tarball + env: + VERSION: ${{ needs.version.outputs.version }} + run: | + docker run --rm -v "$PWD:/src" -w /src -e VERSION "$ARCH_IMAGE" bash -euxc ' + pacman -Syu --noconfirm --needed base-devel pacman-contrib + useradd -m builder && chown -R builder /src + su builder -c " + set -eux + cd /src + scripts/set-package-version.sh $VERSION + cd secureEye/archlinux/secureEye + updpkgsums + makepkg --printsrcinfo > .SRCINFO + " + ' + sudo chown -R "$(id -u):$(id -g)" . + grep -qE "^[[:space:]]*pkgver = ${VERSION}$" secureEye/archlinux/secureEye/.SRCINFO + git diff --stat + + - name: Push to the AUR + env: + AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + VERSION: ${{ needs.version.outputs.version }} + run: | + set -euo pipefail + if [ -z "${AUR_SSH_PRIVATE_KEY:-}" ]; then + echo "::error::AUR_SSH_PRIVATE_KEY secret is not set." + exit 1 + fi + mkdir -p ~/.ssh && chmod 700 ~/.ssh + printf '%s\n' "$AUR_SSH_PRIVATE_KEY" > ~/.ssh/aur + chmod 600 ~/.ssh/aur + ssh-keyscan -t rsa,ed25519 aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null + export GIT_SSH_COMMAND="ssh -i ~/.ssh/aur -o IdentitiesOnly=yes" + + git clone ssh://aur@aur.archlinux.org/secure-eye.git /tmp/aur + cp secureEye/archlinux/secureEye/PKGBUILD \ + secureEye/archlinux/secureEye/.SRCINFO \ + secureEye/archlinux/secureEye/secure-eye.install \ + /tmp/aur/ + + git -C /tmp/aur config user.name "Vedran Hrabar" + git -C /tmp/aur config user.email "vedran.hrabar@outlook.com" + git -C /tmp/aur add -A + if git -C /tmp/aur diff --cached --quiet; then + echo "AUR is already at ${VERSION}; nothing to push." + else + git -C /tmp/aur commit -m "Update to ${VERSION}" + # HEAD:master, not master: secure-eye does not exist on the AUR + # yet, so the first clone is of an empty repo and the local branch + # name comes from init.defaultBranch. Where that is "main", a bare + # "push origin master" fails with "src refspec master does not + # match any". HEAD:master is correct either way. + git -C /tmp/aur push origin HEAD:master + echo "Pushed secure-eye ${VERSION} to the AUR." + fi diff --git a/.gitignore b/.gitignore index e67e5d2..8b077a9 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,9 @@ debian/howdy !.vscode/extensions.json !.vscode/*.code-snippets +# JetBrains IDEs +.idea/ + # Local History for Visual Studio Code .history/ @@ -135,3 +138,4 @@ debian/howdy # Meson subprojects/ +archbuild/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a5ae188 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,164 @@ +# Changelog + +All notable changes to SecureEye are documented here. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Changed + +- SecureEye now ships as a single `secure-eye` package on Debian, RPM and Arch, + replacing `libpam-secureeye`, `secureeye-authd` and the transitional + metapackage. Existing installs are swapped automatically on upgrade. +- The recognition backends are ordinary distribution packages instead of a + bundled virtualenv: `python3-mediapipe` (amd64/x86_64 only) and + `python3-dlib`, from `ppa:vhrabar/tools` on Ubuntu and + `copr:vhrabar/python-extras` on Fedora. Select one with `detector_backend` in + `/etc/secureEye/config.ini`. +- The daemon and the `secureEye` launcher run on the system Python interpreter, + so nothing has to be rebuilt after a Python upgrade. The systemd unit and the + launcher now use the interpreter meson was configured with rather than a + hardcoded virtualenv path. +- Releases no longer carry `.deb` or `.rpm` attachments. Install from the PPA, + COPR or the AUR instead. + +### Added + +- Arch Linux packaging is published to the AUR as `secure-eye`. +- `CHANGELOG.md` is the single source of truth for release notes. + `scripts/set-package-version.sh` renders a release's section into the Debian + and RPM changelogs, and `scripts/changelog-section.sh` extracts it for the + GitHub release body. +- A single release workflow that runs the tests, verifies the deb, RPM and Arch + packages build and install, and only then publishes to the Launchpad PPA, + COPR and the AUR. COPR builds are pinned to the released tag. +- A Prepare release workflow that sets the version, tags it and drafts the + release from the changelog. + +### Removed + +- The install-time virtualenv, the vendored Python wheels and the dpkg trigger + that rebuilt the virtualenv whenever python3 was upgraded. + +### Fixed + +- Launchpad PPA uploads carry the real changelog entries; every upload + previously read "New upstream release". +- Fedora and RHEL install instructions: the package ships no systemd preset, so + `secureeye-authd.service` has to be enabled manually. The README claimed + installation enabled it. +- The CLI reference lists the `set` command. + +## [0.1.3] - 2026-07-23 + +First Arch release: SecureEye now packages and installs on Arch-like +distributions, alongside the existing Debian/Ubuntu/RPM support. + +### Fixed + +- Implemented detector caching and reduced time constraints on CPU-based + systems. + +## [0.1.2] - 2026-07-05 + +First RPM release: SecureEye now packages and installs on Fedora and +RHEL-family distributions, alongside the existing Debian/Ubuntu support. + +### Added + +- RPM packaging mirroring the Debian split layout: three packages — + `libpam-secureeye` (PAM module), `secureeye-authd` (daemon plus the Python + recognition runtime), and the `secure-eye` transitional metapackage. +- COPR distribution (`vhrabar/SecureEye`): install on Fedora with + `dnf copr enable vhrabar/SecureEye && dnf install secure-eye`. +- GitHub Actions workflow building RPMs for x86_64, x86_64-v3 and aarch64, + attaching them to GitHub releases, with an install smoke test. +- `scripts/build-rpm.sh`, to build the RPMs in a Fedora container. +- aarch64 dlib backend: since Fedora ships no `python3-dlib` and there is no + prebuilt aarch64 wheel, the dlib recognition wheel (pinned to 20.0.1) is + compiled from source during the RPM build. + +## [0.1.1] - 2026-07-02 + +### Fixed + +- MediaPipe no longer uses a non-existing PipAudio on some configurations. + +## [0.1.0] - 2026-06-27 + +Initial release, tagged `v0.1.0-alpha`. SecureEye is a modern reimplementation +of facial authentication for Linux, derived from +[Howdy](https://github.com/boltgolt/howdy). + +This is an early, pre-1.0 release. Facial recognition is a convenience factor +and should not be relied on as a sole authentication method. + +### Highlights + +- New detection pipeline: dlib replaced by MediaPipe and FaceNet embeddings, + with dlib still available as an optional backend. +- Fail-safe PAM architecture: a minimal C/C++ PAM module talks to a Python + daemon over a UNIX socket, keeping Python and MediaPipe out of the + authentication path. +- Split packaging into separate PAM, daemon and transitional metapackages. +- Config-driven CLI for managing models and configuration. + +### Added + +Project and governance: + +- Forked and rebranded Howdy as SecureEye, preserving the derived work through + git history, with a `NOTICE` and bundled MIT licence for the inherited code. +- Added a Code of Conduct, Contributing guide and Security policy. +- Configured clang-tidy, ruff and pyproject tooling. + +Detection: + +- Replaced the dlib pipeline with a MediaPipe-based face detector, using + FaceNet embeddings and new preprocessing utilities. dlib is retained as an + optional backend. +- Added a config-driven detector factory with lazy imports, so the CLI and GUI + tools only load the backend they actually need. + +Architecture — the PAM and authd split: + +- Split authentication into a minimal C/C++ PAM module and a Python + authentication daemon communicating over a UNIX-domain socket. This keeps + Python and MediaPipe out of the PAM path, so authentication fails safe. +- Added the `secureeye-authd` daemon: socket server, bounded request + validation, versioned IPC protocol (v1) encode/decode, and cooperative + cancellation with a timeout budget. +- Reworked the PAM module into an IPC client that enforces a wait timeout and + falls back to password when the daemon is unavailable. +- Refactored the former `compare.py` monolith into focused modules: errors, + types, frame ops, matching, model store, auth session and UI bridge. + +CLI: + +- Config-driven entrypoint with dynamic module loading and model-management + commands: `add`, `clear`, `config`, `disable`, `list`, `remove`, `snapshot`, + `test` and `version`. + +Packaging: + +- Split into three packages: `libpam-secureeye` (PAM module only), + `secureeye-authd` (daemon and Python runtime) and `secure-eye` (transitional + metapackage). +- Installs a systemd service and sysusers entry for the daemon. +- Vendors pinned Python dependencies as wheels and builds the runtime venv on + the target host at install time; the venv is rebuilt on python3 upgrades via + a dpkg trigger. + +Documentation: + +- Added architecture, IPC protocol v1 and PAM behaviour-matrix docs. +- Expanded the README with installation, usage and Docker PAM smoke-test + instructions. + +CI and containers: + +- Added a GitHub Actions test workflow, CodeQL analysis and Dependabot. +- Added a Dockerfile and Compose setup for the test environment and the PAM + `sudo` smoke-test flows. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b17dc1..7a974d4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -78,6 +78,17 @@ Before submitting a PR: * Ensure code builds and runs * Add or update tests if applicable * Keep PRs focused on a single concern +* Note user-visible changes under `## [Unreleased]` in `CHANGELOG.md` + +### Changelog + +`CHANGELOG.md` is the single source of truth for release notes, in +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format. Do not edit +`secureEye/debian/changelog` or the `%changelog` in `secureEye/rpm/secure-eye.spec` +by hand: on release, `scripts/set-package-version.sh` renames `## [Unreleased]` +to the tag and renders that section into both, in each format's required +layout. A release with no matching section still succeeds, but ships only +"New upstream release X.Y.Z." to users. PRs may be rejected if they: diff --git a/README.md b/README.md index 5c62097..330ab7f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Early development. Core architecture and modules are being actively built. These mirror the package `Build-Depends` in `secureEye/debian/control`: - Meson 0.64 or higher, Ninja, pkg-config, a C++ compiler (`build-essential`) -- Python 3 with pip (`python3`, `python3-pip`) +- Python 3 (`python3`) - `libpam0g-dev`, `libevdev-dev`, `libinih-dev` (INIReader is provided by `libinih-dev`; it is **not** downloaded, the build runs with `--wrap-mode=nodownload`) @@ -57,7 +57,7 @@ On **Debian / Ubuntu**: ```bash sudo apt-get update && sudo apt-get install -y \ meson ninja-build pkg-config build-essential \ - python3 python3-pip python3-venv \ + python3 \ libpam0g-dev libinih-dev libevdev-dev ``` @@ -80,46 +80,56 @@ meson compile -C build > builds (`.deb`, `.rpm` or the AUR package). Meson's default prefix is > `/usr/local`, and `/usr/local/lib/...` shadows the packaged `/usr/lib/...` > systemd unit (and `/usr/local/bin` shadows `/usr/bin`), which breaks the -> daemon and CLI. On Debian and Fedora a bare `meson install` also does **not** -> create the recognition virtualenv, that is built by the `secureeye-authd` -> package at install time, so the daemon will not start. For a working system -> install, build and install your distribution's packages below. +> daemon and CLI. A bare `meson install` also does **not** pull in the +> recognition backends (`python3-mediapipe` / `python3-dlib`) that the packages +> depend on, so the daemon will not start. For a working system install, build +> and install your distribution's packages below. ### Debian / Ubuntu & derivatives -SecureEye ships as three packages: +SecureEye ships as a single `secure-eye` package containing the C/C++ PAM +module, the `secureeye-authd` daemon, the Python recognition runtime and the +`secureEye` CLI. It replaces the older `libpam-secureeye` / `secureeye-authd` +split, so `apt` swaps those out on upgrade. -- `libpam-secureeye` — the C/C++ PAM module (no Python) -- `secureeye-authd` — the authentication daemon and Python recognition runtime -- `secure-eye` — a transitional metapackage that depends on both - -Download the latest `.deb` files from the -[GitHub releases page](https://github.com/vhrabar/SecureEye/releases) and -install all of them together so dependencies (including `python3-venv`) resolve: +The recognition backends are ordinary packages rather than a bundled +virtualenv, and they live in `ppa:vhrabar/tools`, so add that repository first +either way: ```bash -sudo apt install ./libpam-secureeye_*.deb ./secureeye-authd_*.deb ./secure-eye_*.deb +sudo add-apt-repository ppa:vhrabar/tools +sudo apt update +sudo apt install secure-eye ``` -On install, `secureeye-authd` builds its recognition virtualenv from the bundled -wheels (this takes a short while) and enables the `secureeye-authd.service`. +- `python3-mediapipe` — the default backend, **amd64 only** +- `python3-dlib` — the alternative backend, available on every architecture -### PPA -```bash -sudo add-apt-repository ppa:vhrabar/tools -sudo apt update && sudo apt install secure-eye -``` +Unlike the RPM and Arch packages, the Debian package enables and starts +`secureeye-authd.service` for you on install. ### Fedora, RHEL & RPM-based systems -The easiest way is the -[COPR repository](https://copr.fedorainfracloud.org/coprs/vhrabar/SecureEye/). +SecureEye ships as a single `secure-eye` package containing the C/C++ PAM +module, the `secureeye-authd` daemon, the Python recognition runtime and the +`secureEye` CLI. It obsoletes the older `libpam-secureeye` / `secureeye-authd` +split, so `dnf` swaps those out on upgrade. + +The recognition backends are ordinary packages rather than a bundled +virtualenv, and they live in the +[`vhrabar/python-extras`](https://copr.fedorainfracloud.org/coprs/vhrabar/python-extras/) +COPR, so enable it alongside the +[SecureEye COPR](https://copr.fedorainfracloud.org/coprs/vhrabar/SecureEye/): + +- `python3-mediapipe` — the default backend, **x86_64 only** +- `python3-dlib` — the alternative backend, available on every architecture On **Fedora**: ```bash sudo dnf copr enable vhrabar/SecureEye +sudo dnf copr enable vhrabar/python-extras sudo dnf install secure-eye ``` @@ -132,19 +142,16 @@ sudo dnf copr enable vhrabar/SecureEye sudo dnf install secure-eye ``` -This installs the same three packages as on Debian (`libpam-secureeye`, -`secureeye-authd`, `secure-eye`). On install, `secureeye-authd` builds its -recognition virtualenv from the bundled wheels and enables the -`secureeye-authd.service`. - -Alternatively, download the `.rpm` files from the -[GitHub releases page](https://github.com/vhrabar/SecureEye/releases) and install -them together: +The package ships no systemd preset, so `%systemd_post` leaves the unit +disabled. Start it yourself: ```bash -sudo dnf install ./libpam-secureeye-*.rpm ./secureeye-authd-*.rpm ./secure-eye-*.rpm +sudo systemctl enable --now secureeye-authd.service ``` +On architectures without `python3-mediapipe` (anything other than x86_64), +switch the backend before first use with `sudo secureEye config`. + > [!NOTE] > On Fedora/RHEL there is no `pam-auth-update`, so the PAM module is **not** > enabled automatically. Enable it as shown in **Usage step 3b** below (the @@ -152,37 +159,40 @@ sudo dnf install ./libpam-secureeye-*.rpm ./secureeye-authd-*.rpm ./secure-eye-* ### Arch Linux & derivatives -SecureEye is packaged for the AUR as `secureeye`, which builds two packages: - -- `libpam-secureeye`: the C/C++ PAM module (no Python) -- `secureeye-authd`: the authentication daemon and Python recognition runtime +SecureEye is packaged for the AUR as a single `secure-eye` package containing +the C/C++ PAM module, the `secureeye-authd` daemon, the Python recognition +runtime and the `secureEye` CLI. It replaces the older `libpam-secureeye` / +`secureeye-authd` split, so pacman swaps those out on upgrade. -There is no transitional metapackage; install both. With an AUR helper: +With an AUR helper: ```bash -paru -S libpam-secureeye secureeye-authd # or: yay -S ... +paru -S secure-eye # or: yay -S ... ``` -Or manually with `makepkg` (the recognition dependencies `python-mediapipe` -and `python-sounddevice` also come from the AUR and must be built first): +Or manually with `makepkg` (the recognition backends `python-dlib` and +`python-mediapipe` also come from the AUR and are built first): ```bash -git clone https://aur.archlinux.org/secureeye.git -cd secureeye +git clone https://aur.archlinux.org/secure-eye.git +cd secure-eye makepkg -si ``` -You can also build straight from a checkout of this repository: +The packaging sources also live in this repository, under +`secureEye/archlinux/secureEye/`: ```bash cd secureEye/archlinux/secureEye makepkg -si ``` -Unlike the `.deb`/`.rpm` packages, the Arch build does **not** bundle a recognition virtualenv: every dependency is a -real package and the daemon runs on the system interpreter, so there is nothing to rebuild after a Python upgrade. On -`aarch64` there is no MediaPipe, so the package depends on -`python-dlib` and ships `detector_backend = dlib` in the default config. +As with the `.deb` and `.rpm` packages, the Arch build does **not** bundle a +recognition virtualenv: every dependency is a real package and the daemon runs +on the system interpreter, so there is nothing to rebuild after a Python +upgrade. `python-mediapipe` is the default backend and is x86_64 only; +`python-dlib` is the alternative. On other architectures set +`detector_backend = dlib` in `/etc/secureEye/config.ini` before first use. Following Arch policy, the service is **not** started for you: @@ -284,6 +294,7 @@ secureEye [-U user] [-y] command [argument] | `disable` | Disable or enable SecureEye | | `list` | List all saved face models for a user | | `remove` | Remove a specific model for a user | +| `set` | Set a config option (`secureEye set certainty 3`) | | `snapshot` | Take a snapshot of your camera input | | `test` | Test the camera and recognition methods | | `version` | Print the current version number | @@ -301,7 +312,8 @@ secureEye [-U user] [-y] command [argument] ### Requirements * Python 3.12+ -* pip / virtualenv +* [uv](https://docs.astral.sh/uv/) — `uv sync` provisions the environment; see + the [Contributing guide](CONTRIBUTING.md) ### Docker PAM Automation diff --git a/requirements-vendor.txt b/requirements-vendor.txt deleted file mode 100644 index 14a4b2c..0000000 --- a/requirements-vendor.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Wheels vendored into the secureeye-authd venv (installed with --no-deps into a -# --system-site-packages venv). The heavy, ABI-specific dependencies -# (numpy, opencv, matplotlib) are provided by the distro instead — see the -# secureeye-authd Depends in debian/control — which keeps the vendored payload -# small (~12 MB) and Python-version independent. requirements.txt remains the -# full list for local development. -mediapipe==0.10.33 -ffmpeg-python==0.2.0 -keyboard==0.13.5 -absl-py~=2.3 -flatbuffers~=25.9 -sounddevice~=0.5 -# future: required by ffmpeg-python. Vendored here because python3-future was -# dropped from recent Ubuntu/Debian archives, so it can no longer be a Depends. -future~=1.0 \ No newline at end of file diff --git a/scripts/build-rpm.sh b/scripts/build-rpm.sh index b1c3a45..dd47bdc 100644 --- a/scripts/build-rpm.sh +++ b/scripts/build-rpm.sh @@ -1,7 +1,10 @@ #!/usr/bin/env bash -# Build SecureEye RPMs inside a Fedora container (GitHub release artifacts). +# Build SecureEye RPMs inside a Fedora container, to verify the packaging. +# Nothing is attached to GitHub releases; dist/*.rpm is kept only as a +# short-lived workflow artifact. COPR does the real builds. # Env: PKG_VERSION (required), MARCH, REL_SUFFIX. Output: dist/*.rpm. -# The recognition wheels are fetched/compiled per-arch by the spec's %build. +# Nothing is vendored: the recognition backends are runtime Requires resolved +# from copr:vhrabar/python-extras at install time. set -euxo pipefail : "${PKG_VERSION:?PKG_VERSION is required}" @@ -11,14 +14,18 @@ dnf install -y --setopt=install_weak_deps=False \ rpm-build rpmdevtools rpmlint git-core tar gzip dnf-plugins-core git config --global --add safe.directory "$PWD" -# Install BuildRequires (honours the spec's %ifnarch dlib toolchain on aarch64). + +scripts/set-package-version.sh "$PKG_VERSION" +git -c user.name=ci -c user.email=ci@localhost \ + commit -qam "generated packaging for ${PKG_VERSION}" --allow-empty + dnf builddep -y --define "pkg_version ${PKG_VERSION}" "$SPEC" rpmdev-setuptree git archive --format=tar.gz \ --prefix="secure-eye-${PKG_VERSION}/" \ -o "$HOME/rpmbuild/SOURCES/secure-eye-${PKG_VERSION}.tar.gz" HEAD -cp secureEye/rpm/secureeye-authd.sysusers "$HOME/rpmbuild/SOURCES/" +cp secureEye/rpm/secure-eye.sysusers "$HOME/rpmbuild/SOURCES/" rpmbuild -bb "$SPEC" \ --define "pkg_version ${PKG_VERSION}" \ diff --git a/scripts/changelog-section.sh b/scripts/changelog-section.sh new file mode 100755 index 0000000..3e60279 --- /dev/null +++ b/scripts/changelog-section.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Print one CHANGELOG.md section as plain markdown, for a GitHub release body. +# +# scripts/changelog-section.sh 0.1.4 +# scripts/changelog-section.sh Unreleased +# +set -euo pipefail + +want="${1:?usage: changelog-section.sh }" +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +changelog_md="$root/CHANGELOG.md" + +[ -f "$changelog_md" ] || { echo "::error::$changelog_md is missing" >&2; exit 1; } + +out="$(awk -v want="$want" ' + function hdrver(s, i) { + sub(/^##[ \t]+/, "", s) + if (substr(s, 1, 1) == "[") { + s = substr(s, 2); i = index(s, "]") + if (i > 0) s = substr(s, 1, i - 1) + } else { i = index(s, " "); if (i > 0) s = substr(s, 1, i - 1) } + return s + } + /^## / { insec = (hdrver($0) == want); next } + insec { print } +' "$changelog_md" | sed -e '/./,$!d' | tac | sed -e '/./,$!d' | tac)" + +[ -n "$out" ] || { echo "::error::CHANGELOG.md has no [$want] section, or it is empty" >&2; exit 1; } +printf '%s\n' "$out" diff --git a/scripts/publish-ppa.sh b/scripts/publish-ppa.sh index 9cec668..d648a0e 100755 --- a/scripts/publish-ppa.sh +++ b/scripts/publish-ppa.sh @@ -193,28 +193,6 @@ if [[ -n $debian_dir ]]; then fi -echo "::group::Vendoring Python wheels..." -requirements_file="${REQUIREMENTS:-requirements-vendor.txt}" -if [[ "$requirements_file" != /* ]]; then - requirements_file="$workspace/$requirements_file" -fi -if [[ ! -f "$requirements_file" ]]; then - echo "requirements file not found: $requirements_file" >&2 - exit 1 -fi -wheels_cache=/tmp/workspace/wheels -rm -rf "$wheels_cache" && mkdir -p "$wheels_cache" - -python3 -m pip download \ - --only-binary=:all: --no-cache-dir --no-deps \ - --platform manylinux_2_28_x86_64 \ - --platform manylinux_2_17_x86_64 \ - --platform manylinux2014_x86_64 \ - --dest "$wheels_cache" \ - -r "$requirements_file" -ls -la "$wheels_cache" -echo "::endgroup::" - series_index=0 for s in $SERIES; do series_index=$((series_index + 1)) @@ -245,10 +223,6 @@ for s in $SERIES; do fi fi - # Ship the vendored wheels inside the source package (offline build input). - mkdir -p debian/wheels - cp "$wheels_cache"/*.whl debian/wheels/ - # Extract the package name from the debian changelog package=$(dpkg-parsechangelog --show-field Source) pkg_version=$(dpkg-parsechangelog --show-field Version | cut -d- -f1) @@ -266,8 +240,18 @@ for s in $SERIES; do echo "New version: $newversion" + if [[ "$is_native" == "1" && -f "$debian_dir/changelog" ]]; then + sed -E "0,/^[^ ]+ \([^)]+\) [^;]+; urgency=[^ ]+/s//$package ($newversion) $s; urgency=medium/" \ + "$debian_dir/changelog" > debian/changelog + + head -1 debian/changelog | grep -qF "($newversion)" || { + echo "Failed to set the version in the native changelog" >&2 + exit 1 + } + echo "Changelog top entry:" + head -8 debian/changelog # Use provided changelog if KEEP_CHANGELOG is set - if [[ -n $KEEP_CHANGELOG ]]; then + elif [[ -n $KEEP_CHANGELOG ]]; then # Ensure the changelog exists in the $DEBIAN_DIR if [[ ! -f $debian_dir/changelog ]]; then echo "KEEP_CHANGELOG is set, but the changelog file does not exist" diff --git a/scripts/set-package-version.sh b/scripts/set-package-version.sh new file mode 100755 index 0000000..b4dd381 --- /dev/null +++ b/scripts/set-package-version.sh @@ -0,0 +1,189 @@ +#!/usr/bin/env bash + +set -euo pipefail + +version="${1:?usage: set-package-version.sh }" +case "$version" in + [0-9]*) ;; + *) echo "::error::version must start with a digit, got '$version'" >&2; exit 1 ;; +esac + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +changelog="$root/secureEye/debian/changelog" +spec="$root/secureEye/rpm/secure-eye.spec" +pkgbuild="$root/secureEye/archlinux/secureEye/PKGBUILD" +changelog_md="$root/CHANGELOG.md" + +msg="${CHANGELOG_MSG:-New upstream release ${version}.}" +name="${DEBFULLNAME:-Vedran Hrabar}" +mail="${DEBEMAIL:-vedran.hrabar@outlook.com}" + +# Render the CHANGELOG.md section for $1 in style $2 (deb|rpm). +render_changelog() { + local want=$1 style=$2 out + [ -f "$changelog_md" ] || return 1 + out="$(awk -v want="$want" -v style="$style" ' + # Version token out of a "## [1.2.3] - date" / "## 1.2.3" heading. + function hdrver(s, i) { + sub(/^##[ \t]+/, "", s) + if (substr(s, 1, 1) == "[") { + s = substr(s, 2) + i = index(s, "]") + if (i > 0) s = substr(s, 1, i - 1) + } else { + i = index(s, " ") + if (i > 0) s = substr(s, 1, i - 1) + } + return s + } + # Greedy wrap so generated entries stay inside 76 columns. + function wrap(prefix, cont, text, words, n, i, line, started) { + n = split(text, words, /[ \t]+/) + started = 0 + for (i = 1; i <= n; i++) { + if (!started) { line = prefix words[i]; started = 1 } + else if (length(line) + 1 + length(words[i]) <= 76) { line = line " " words[i] } + else { print line; line = cont words[i] } + } + if (started) print line + } + function flush( text) { + if (buf == "") return + if (style == "deb") { + if (group != "") { + if (group != shown) { print " * " group; shown = group } + wrap(" - ", " ", buf) + } else { + wrap(" * ", " ", buf) + } + } else { + text = (group != "") ? group ": " buf : buf + wrap("- ", " ", text) + } + buf = "" + } + /^## / { flush(); insec = (hdrver($0) == want); group = ""; shown = ""; next } + !insec { next } + /^### / { flush(); group = $0; sub(/^###[ \t]+/, "", group); next } + /^[ \t]*$/ { flush(); next } + /^[ \t]*[-*][ \t]+/ { flush(); buf = $0; sub(/^[ \t]*[-*][ \t]+/, "", buf); next } + # Anything else continues the current bullet, or starts a bare one. + { + line = $0; sub(/^[ \t]+/, "", line) + buf = (buf == "") ? line : buf " " line + } + END { flush() } + ' "$changelog_md")" + [ -n "$out" ] || return 1 + printf '%s\n' "$out" +} + +# Rename "## [Unreleased]" to "## [X.Y.Z] - " and open a fresh empty unr +promote_unreleased() { + local tmp + render_changelog Unreleased deb >/dev/null || return 1 + tmp="$(mktemp)" + awk -v ver="$version" -v today="$(date +%F)" ' + /^##[ \t]+\[?[Uu]nreleased\]?/ && !done { + print "## [Unreleased]" + print "" + print "## [" ver "] - " today + done = 1 + next + } + { print } + ' "$changelog_md" > "$tmp" + mv "$tmp" "$changelog_md" + echo "CHANGELOG.md: promoted [Unreleased] -> [$version]" +} + +# Date for $version, taken from its "## [X.Y.Z] - YYYY-MM-DD" heading so the +# generated entries are identical in CI, on COPR and locally. Nothing is +# committed back, so a clock-derived date would make every build differ. +entry_date() { + local fmt=$1 d + d="$(awk -v want="$version" ' + $0 ~ "^##[ \t]+\\[?" want "\\]?([ \t]|$)" { + if (match($0, /[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/)) + print substr($0, RSTART, RLENGTH) + exit + }' "$changelog_md")" + if [ -z "$d" ]; then + echo "::warning::CHANGELOG.md [$version] has no date; using today's, so this build is not reproducible." >&2 + d="$(date -u +%F)" + fi + LC_ALL=C date -u -d "$d 00:00:00" "+$fmt" +} + +deb_body="" +rpm_body="" +render_changelog "$version" deb >/dev/null || promote_unreleased || true +if deb_body="$(render_changelog "$version" deb)" \ + && rpm_body="$(render_changelog "$version" rpm)"; then + echo "CHANGELOG.md: using the [$version] section" +else + echo "::warning::CHANGELOG.md has no [$version] section and no [Unreleased] content; falling back to '$msg'" + deb_body=" * ${msg}" + rpm_body="- ${msg}" +fi + +# --- debian --------------------------------------------------------------- +current="$(sed -n '1s/^[^ ]* (\([^)]*\)).*/\1/p' "$changelog")" +if [ "$current" = "$version" ]; then + echo "debian/changelog already at $version" +else + tmp="$(mktemp)" + { + printf 'secure-eye (%s) unstable; urgency=medium\n\n' "$version" + printf '%s\n\n' "$deb_body" + printf ' -- %s <%s> %s\n\n' "$name" "$mail" \ + "$(entry_date '%a, %d %b %Y %H:%M:%S +0000')" + cat "$changelog" + [ -n "$(tail -c 1 "$changelog")" ] && printf '\n' + } > "$tmp" + mv "$tmp" "$changelog" + echo "debian/changelog: $current -> $version" +fi + +# --- rpm ------------------------------------------------------------------ + +sed -i -E "s/^(%\{!\?pkg_version:%global pkg_version )[^}]*(\})/\1${version}\2/" "$spec" +grep -q "pkg_version ${version}}" "$spec" \ + || { echo "::error::failed to set pkg_version in $spec" >&2; exit 1; } + +if grep -qE "^\* .* - ${version}-" "$spec"; then + echo "rpm spec: %changelog already has $version" +else + tmp="$(mktemp)" + { + printf '* %s %s <%s> - %s-1\n' \ + "$(entry_date '%a %b %d %Y')" "$name" "$mail" "$version" + printf '%s\n\n' "$rpm_body" + } > "$tmp" + awk -v entryfile="$tmp" ' + /^%changelog$/ && !done { + print + while ((getline line < entryfile) > 0) print line + close(entryfile) + done = 1 + next + } + { print } + ' "$spec" > "$tmp.spec" + mv "$tmp.spec" "$spec" + rm -f "$tmp" +fi +echo "rpm spec: pkg_version -> $version" + +# --- arch ----------------------------------------------------------------- + +sed -i -E "s/^pkgver=.*/pkgver=${version}/" "$pkgbuild" +sed -i -E "s/^pkgrel=.*/pkgrel=1/" "$pkgbuild" +if [ -n "${SRC_SHA256:-}" ]; then + sed -i -E "0,/^sha256sums=\('[^']*'/s//sha256sums=('${SRC_SHA256}'/" "$pkgbuild" + grep -q "sha256sums=('${SRC_SHA256}'" "$pkgbuild" \ + || { echo "::error::failed to set sha256sums[0] in $pkgbuild" >&2; exit 1; } + echo "PKGBUILD: pkgver -> $version, sha256sums[0] -> $SRC_SHA256" +else + echo "PKGBUILD: pkgver -> $version (sha256sums left untouched)" +fi diff --git a/secureEye/archlinux/secureEye/.SRCINFO b/secureEye/archlinux/secureEye/.SRCINFO index 5e863d4..45bc243 100644 --- a/secureEye/archlinux/secureEye/.SRCINFO +++ b/secureEye/archlinux/secureEye/.SRCINFO @@ -1,8 +1,9 @@ -pkgbase = secureeye +pkgbase = secure-eye pkgdesc = Face authentication for Linux - pkgver = 0.1.3 + pkgver = 0.1.2 pkgrel = 1 url = https://github.com/vhrabar/SecureEye + install = secure-eye.install arch = x86_64 arch = aarch64 license = GPL-2.0-only @@ -10,45 +11,34 @@ pkgbase = secureeye makedepends = meson>=0.64 makedepends = ninja makedepends = pkgconf - makedepends = pam - makedepends = libevdev - makedepends = libinih - makedepends = python - makedepends = systemd - source = secureeye-0.1.3.tar.gz::https://github.com/vhrabar/SecureEye/archive/v0.1.3.tar.gz - source = secureeye-authd.install - sha256sums = c848207d44068849a13150a8b47a3d45806a9a43b9c868d1f5915ed8c2cf2d18 - sha256sums = e68c31756e7196406ec24bd55cdab1af473a35c2abdd4c44a2ca9ace525beb73 - -pkgname = libpam-secureeye - pkgdesc = PAM module for SecureEye face authentication depends = pam depends = libevdev depends = libinih depends = gcc-libs depends = glibc - optdepends = secureeye-authd: authentication daemon, without it the module always fails - -pkgname = secureeye-authd - pkgdesc = SecureEye authentication daemon, CLI and Python runtime components - install = secureeye-authd.install depends = python depends = python-numpy depends = python-opencv depends = python-matplotlib depends = python-cffi - depends = python-absl - depends = python-flatbuffers - depends = portaudio + depends = python-dlib depends = v4l-utils depends = systemd - optdepends = libpam-secureeye: PAM integration, required to actually log in with SecureEye - optdepends = python-dlib: dlib recognition backend - optdepends = python-mediapipe: mediapipe recognition backend + optdepends = python-sounddevice: audio device enumeration for the mediapipe backend optdepends = ffmpeg: ffmpeg camera capture backend optdepends = python-ffmpeg-python: ffmpeg camera capture backend optdepends = python-keyboard: hotkey rubberstamp + provides = libpam-secureeye + provides = secureeye-authd + conflicts = libpam-secureeye + conflicts = secureeye-authd + replaces = libpam-secureeye + replaces = secureeye-authd backup = etc/secureEye/config.ini + source = secure-eye-0.1.2.tar.gz::https://github.com/vhrabar/SecureEye/archive/v0.1.2.tar.gz + source = secure-eye.install + sha256sums = c848207d44068849a13150a8b47a3d45806a9a43b9c868d1f5915ed8c2cf2d18 + sha256sums = f07bfc5fd27d2e649eebafbccaaefe1be5cce3606363df9a74c7700098476d8f depends_x86_64 = python-mediapipe - depends_x86_64 = python-sounddevice - depends_aarch64 = python-dlib + +pkgname = secure-eye diff --git a/secureEye/archlinux/secureEye/PKGBUILD b/secureEye/archlinux/secureEye/PKGBUILD index 355b56a..a1f99e5 100644 --- a/secureEye/archlinux/secureEye/PKGBUILD +++ b/secureEye/archlinux/secureEye/PKGBUILD @@ -1,38 +1,52 @@ # Maintainer: Vedran Hrabar -pkgbase=secureeye -pkgname=('libpam-secureeye' 'secureeye-authd') +pkgname=secure-eye pkgver=0.1.2 pkgrel=1 pkgdesc="Face authentication for Linux" arch=('x86_64' 'aarch64') url="https://github.com/vhrabar/SecureEye" license=('GPL-2.0-only' 'MIT') -makedepends=( - 'meson>=0.64' - 'ninja' - 'pkgconf' +depends=( 'pam' 'libevdev' 'libinih' + 'gcc-libs' + 'glibc' 'python' + 'python-numpy' + 'python-opencv' + 'python-matplotlib' + 'python-cffi' + 'python-dlib' + 'v4l-utils' 'systemd' ) -source=("$pkgbase-$pkgver.tar.gz::$url/archive/v${pkgver}.tar.gz" - 'secureeye-authd.install') +depends_x86_64=('python-mediapipe') + +makedepends=( + 'meson>=0.64' + 'ninja' + 'pkgconf' +) +optdepends=( + 'python-sounddevice: audio device enumeration for the mediapipe backend' + 'ffmpeg: ffmpeg camera capture backend' + 'python-ffmpeg-python: ffmpeg camera capture backend' + 'python-keyboard: hotkey rubberstamp' +) +provides=('libpam-secureeye' 'secureeye-authd') +conflicts=('libpam-secureeye' 'secureeye-authd') +replaces=('libpam-secureeye' 'secureeye-authd') +backup=('etc/secureEye/config.ini') +install=secure-eye.install +source=("$pkgname-$pkgver.tar.gz::$url/archive/v${pkgver}.tar.gz" + 'secure-eye.install') sha256sums=('c848207d44068849a13150a8b47a3d45806a9a43b9c868d1f5915ed8c2cf2d18' - 'e68c31756e7196406ec24bd55cdab1af473a35c2abdd4c44a2ca9ace525beb73') + 'f07bfc5fd27d2e649eebafbccaaefe1be5cce3606363df9a74c7700098476d8f') _srcname="SecureEye-$pkgver" -prepare() { - cd "$_srcname" - - # The deb/rpm packages run the daemon from a wheel venv built at install - sed -i 's|/usr/lib/secureeye-authd/venv/bin/python3|/usr/bin/python3|' \ - secureEye/src/systemd/secureeye-authd.service.in -} - build() { meson setup "$_srcname" build \ --wrap-mode=nodownload \ @@ -42,68 +56,19 @@ build() { --sysconfdir=/etc \ --localstatedir=/var \ -Dinstall_pam_config=false \ + -Dpython_path=/usr/bin/python3 \ -Dconfig_dir=/etc/secureEye \ -Duser_models_dir=/etc/secureEye/models meson compile -C build } -package_libpam-secureeye() { - pkgdesc="PAM module for SecureEye face authentication" - depends=('pam' 'libevdev' 'libinih' 'gcc-libs' 'glibc') - optdepends=('secureeye-authd: authentication daemon, without it the module always fails') - - # Install tags keep each split package to its own files. - meson install -C build --destdir "$pkgdir" --tags pam_module - - install -Dm644 "$_srcname/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE" - install -Dm644 "$_srcname/licenses/MIT.txt" "$pkgdir/usr/share/licenses/$pkgname/MIT.txt" - install -Dm644 "$_srcname/NOTICE" "$pkgdir/usr/share/licenses/$pkgname/NOTICE" -} - -package_secureeye-authd() { - pkgdesc="SecureEye authentication daemon, CLI and Python runtime components" - depends=( - 'python' - 'python-numpy' - 'python-opencv' - 'python-matplotlib' - 'python-cffi' - 'python-absl' - 'python-flatbuffers' - 'portaudio' - 'v4l-utils' - 'systemd' - ) - # amd64 -> mediapipe (default backend) - # aarch64 -> dlib - depends_x86_64=('python-mediapipe' 'python-sounddevice') - depends_aarch64=('python-dlib') - optdepends=( - 'libpam-secureeye: PAM integration, required to actually log in with SecureEye' - 'python-dlib: dlib recognition backend' - 'python-mediapipe: mediapipe recognition backend' - 'ffmpeg: ffmpeg camera capture backend' - 'python-ffmpeg-python: ffmpeg camera capture backend' - 'python-keyboard: hotkey rubberstamp' - ) - backup=('etc/secureEye/config.ini') - install=secureeye-authd.install - +package() { meson install -C build --destdir "$pkgdir" \ - --tags bin,py_sources,config,systemd,bash_completion,meta,man + --tags pam_module,bin,py_sources,config,systemd,bash_completion,meta,man - # user_models_dir, created by the CLI on first enrolment. install -dm755 "$pkgdir/etc/secureEye/models" - - # The systemd unit runs as the "secureeye" user; systemd's pacman hook - # creates it from this fragment. - install -Dm644 "$_srcname/secureEye/rpm/secureeye-authd.sysusers" \ - "$pkgdir/usr/lib/sysusers.d/secureeye-authd.conf" - - if [[ $CARCH != x86_64 ]]; then - sed -i 's/^detector_backend = mediapipe/detector_backend = dlib/' \ - "$pkgdir/etc/secureEye/config.ini" - fi + install -Dm644 "$_srcname/secureEye/rpm/secure-eye.sysusers" \ + "$pkgdir/usr/lib/sysusers.d/secure-eye.conf" install -Dm644 "$_srcname/LICENSE" "$pkgdir/usr/share/licenses/$pkgname/LICENSE" install -Dm644 "$_srcname/licenses/MIT.txt" "$pkgdir/usr/share/licenses/$pkgname/MIT.txt" diff --git a/secureEye/archlinux/secureEye/secureeye-authd.install b/secureEye/archlinux/secureEye/secure-eye.install similarity index 77% rename from secureEye/archlinux/secureEye/secureeye-authd.install rename to secureEye/archlinux/secureEye/secure-eye.install index 8462e69..46f6dc3 100644 --- a/secureEye/archlinux/secureEye/secureeye-authd.install +++ b/secureEye/archlinux/secureEye/secure-eye.install @@ -14,6 +14,13 @@ post_install() { Configuration lives in /etc/secureEye/config.ini. EOF + if [ "$(uname -m)" != "x86_64" ]; then + cat <<'EOF' +==> python-mediapipe is x86_64 only. On this architecture set + detector_backend = dlib + in /etc/secureEye/config.ini before first use. +EOF + fi } post_upgrade() { diff --git a/secureEye/debian/changelog b/secureEye/debian/changelog index ffd56f7..37ea536 100644 --- a/secureEye/debian/changelog +++ b/secureEye/debian/changelog @@ -3,4 +3,4 @@ secure-eye (0.1.1) unstable; urgency=medium * Bugfixes - Fix MediaPipe using non-existent PipAudio on some configurations - -- Vedran Hrabar Thu, 02 Jul 2026 15:14:57 +0200 \ No newline at end of file + -- Vedran Hrabar Thu, 02 Jul 2026 15:14:57 +0200 diff --git a/secureEye/debian/control b/secureEye/debian/control index 60f96b6..b798730 100644 --- a/secureEye/debian/control +++ b/secureEye/debian/control @@ -1,5 +1,5 @@ Source: secure-eye -Section: misc +Section: admin Priority: optional Maintainer: Vedran Hrabar Homepage: https://github.com/vhrabar/secureEye @@ -11,42 +11,38 @@ Build-Depends: ninja-build, pkg-config, python3, - python3-pip, libpam0g-dev, libevdev-dev, libinih-dev Vcs-Git: https://github.com/vhrabar/secureEye -Package: libpam-secureeye -Section: admin -Architecture: any -Depends: ${misc:Depends}, ${shlibs:Depends}, libpam0g -Description: SecureEye PAM module - C/C++ PAM module for SecureEye authentication integration. - This package contains only PAM-facing components. - -Package: secureeye-authd -Section: admin +Package: secure-eye Architecture: any Depends: ${misc:Depends}, - ${secureeye:Backend}, + ${shlibs:Depends}, + libpam0g, python3, - python3-venv, python3-numpy, python3-opencv, python3-matplotlib, python3-cffi, - libportaudio2, + python3-dlib, + python3-mediapipe [amd64], v4l-utils -Recommends: libpam-secureeye -Description: SecureEye authentication daemon and Python runtime components - Helper daemon and Python runtime stack used by SecureEye face authentication. - Includes systemd service assets and CLI tooling. - -Package: secure-eye -Section: oldlibs -Architecture: all -Depends: ${misc:Depends}, libpam-secureeye, secureeye-authd -Description: Transitional package for SecureEye split packages - This transitional package depends on the split SecureEye components. +Recommends: python3-sounddevice +Suggests: python3-ffmpeg, python3-keyboard +Provides: libpam-secureeye, secureeye-authd +Replaces: libpam-secureeye (<< 0.1.2~), secureeye-authd (<< 0.1.2~) +Breaks: libpam-secureeye (<< 0.1.2~), secureeye-authd (<< 0.1.2~) +Description: SecureEye face authentication for Linux + SecureEye authenticates Linux users with their face. This package ships the + complete stack: the C/C++ PAM module, the secureeye-authd authentication + daemon it talks to over a UNIX socket, the Python recognition runtime and the + secureEye command line tool. + . + Recognition backends come from the distribution rather than a bundled + virtualenv: python3-mediapipe (amd64 only) is the default backend and + python3-dlib is the alternative, selectable via detector_backend in + /etc/secureEye/config.ini. On architectures without mediapipe, set + detector_backend = dlib. diff --git a/secureEye/debian/install b/secureEye/debian/install deleted file mode 100644 index 1faea64..0000000 --- a/secureEye/debian/install +++ /dev/null @@ -1,4 +0,0 @@ -# Legacy monolithic manifest intentionally left empty. -# Split manifests: -# - debian/libpam-secureeye.install -# - debian/secureeye-authd.install diff --git a/secureEye/debian/libpam-secureeye.install b/secureEye/debian/libpam-secureeye.install deleted file mode 100644 index 4372d3e..0000000 --- a/secureEye/debian/libpam-secureeye.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/lib/*/security/pam_secureEye.so -debian/secureEye.pam-config usr/share/pam-configs/ - diff --git a/secureEye/debian/not-installed b/secureEye/debian/not-installed index 2ad20fd..0ef556a 100644 --- a/secureEye/debian/not-installed +++ b/secureEye/debian/not-installed @@ -1,4 +1,2 @@ usr/share/dlib-data/Readme.md usr/share/dlib-data/install.sh -usr/share/man/man1/SecureEye.1 - diff --git a/secureEye/debian/postinst b/secureEye/debian/postinst deleted file mode 100755 index 593abec..0000000 --- a/secureEye/debian/postinst +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - configure) - if command -v pam-auth-update >/dev/null 2>&1 && \ - { [ -z "${DPKG_MAINTSCRIPT_PACKAGE:-}" ] || [ "${DPKG_MAINTSCRIPT_PACKAGE:-}" = "libpam-secureeye" ]; }; then - pam-auth-update --package || true - fi - ;; -esac - -exit 0 diff --git a/secureEye/debian/prerm b/secureEye/debian/prerm deleted file mode 100755 index dbbd566..0000000 --- a/secureEye/debian/prerm +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -set -e - -case "$1" in - remove|purge) - if command -v pam-auth-update >/dev/null 2>&1 && \ - { [ -z "${DPKG_MAINTSCRIPT_PACKAGE:-}" ] || [ "${DPKG_MAINTSCRIPT_PACKAGE:-}" = "libpam-secureeye" ]; }; then - pam-auth-update --package || true - fi - ;; -esac - -exit 0 diff --git a/secureEye/debian/rules b/secureEye/debian/rules index fcb613b..d5ce86e 100755 --- a/secureEye/debian/rules +++ b/secureEye/debian/rules @@ -6,21 +6,11 @@ include /usr/share/dpkg/default.mk BUILDDIR = obj-$(DEB_HOST_GNU_TYPE) SRCDIR := $(shell test -f $(CURDIR)/meson.options && echo $(CURDIR) || echo $(CURDIR)/..) -AUTHD_LIBDIR = $(CURDIR)/debian/tmp/usr/lib/secureeye-authd -AUTHD_WHEELS = $(AUTHD_LIBDIR)/wheels -AUTHD_REQS := $(shell test -f $(CURDIR)/requirements-vendor.txt && echo $(CURDIR)/requirements-vendor.txt || echo $(CURDIR)/../requirements-vendor.txt) -VENDORED_WHEELS = $(CURDIR)/debian/wheels - -# amd64(v3) -> mediapipe (default) -# aarch64 -> dlib (temp.) -ifeq ($(DEB_HOST_ARCH),amd64) - BACKEND_DEPENDS = -else - BACKEND_DEPENDS = python3-dlib -endif +# installsysusers: dh_installsysusers is only in the default sequence from +# compat 14, and debian/secure-eye.sysusers creates the daemon's system user. %: - dh $@ + dh $@ --with installsysusers override_dh_clean: dh_clean -X__pycache__ -X.pyc @@ -36,6 +26,7 @@ override_dh_auto_configure: -Dpython.bytecompile=-1 \ -Dinih:with_INIReader=true \ -Dinstall_pam_config=false \ + -Dpython_path=/usr/bin/python3 \ -Dconfig_dir=/etc/secureEye \ -Duser_models_dir=/etc/secureEye/models @@ -44,50 +35,6 @@ override_dh_auto_build: override_dh_auto_install: DESTDIR=$(CURDIR)/debian/tmp meson install -C $(BUILDDIR) - mkdir -p $(AUTHD_WHEELS) -ifeq ($(DEB_HOST_ARCH),amd64) - install -m 0644 $(AUTHD_REQS) $(AUTHD_LIBDIR)/requirements.txt -else - # No mediapipe wheel for this arch: drop it from the venv requirements and - # default the shipped config to the dlib backend. - grep -iv '^[[:space:]]*mediapipe' $(AUTHD_REQS) > $(AUTHD_LIBDIR)/requirements.txt - chmod 0644 $(AUTHD_LIBDIR)/requirements.txt - sed -i 's/^detector_backend = mediapipe/detector_backend = dlib/' \ - $(CURDIR)/debian/tmp/etc/secureEye/config.ini -endif - # Place vendored wheels (offline, e.g. Launchpad); otherwise download them - # with pip for a local network build. The amd64-only mediapipe wheel is - # skipped on other architectures. - if ls $(VENDORED_WHEELS)/*.whl >/dev/null 2>&1; then \ - echo "Using pre-vendored wheels from $(VENDORED_WHEELS)"; \ - for whl in $(VENDORED_WHEELS)/*.whl; do \ - base=$$(basename "$$whl"); \ - if [ "$(DEB_HOST_ARCH)" != "amd64" ] && [ "$${base#mediapipe-}" != "$$base" ]; then \ - echo "Skipping $$base on $(DEB_HOST_ARCH)"; \ - continue; \ - fi; \ - cp "$$whl" $(AUTHD_WHEELS)/; \ - done; \ - else \ - echo "No vendored wheels found; downloading with pip (needs network)"; \ - python3 -m pip wheel --no-cache-dir --no-deps --wheel-dir $(AUTHD_WHEELS) -r $(AUTHD_LIBDIR)/requirements.txt; \ - fi -override_dh_install: - rm -rf debian/libpam-secureeye debian/secureeye-authd debian/secure-eye - dh_install +override_dh_missing: dh_missing --list-missing - -override_dh_dwz: - # Bundled venv contains prebuilt wheel ELF objects that dh_dwz cannot process reliably. - dh_dwz -a -Nsecureeye-authd - -override_dh_strip: - # Skip strip/dbgsym for secureeye-authd bundled venv ELF files from third-party wheels. - dh_strip -a -Nsecureeye-authd - -override_dh_gencontrol: - # Resolve ${secureeye:Backend}: python3-dlib off amd64, empty on amd64. - dh_gencontrol -psecureeye-authd -- -V'secureeye:Backend=$(BACKEND_DEPENDS)' - dh_gencontrol -Nsecureeye-authd - diff --git a/secureEye/debian/secureeye-authd.install b/secureEye/debian/secure-eye.install similarity index 56% rename from secureEye/debian/secureeye-authd.install rename to secureEye/debian/secure-eye.install index 4758f17..7155843 100644 --- a/secureEye/debian/secureeye-authd.install +++ b/secureEye/debian/secure-eye.install @@ -1,10 +1,9 @@ usr/bin/secureEye +usr/lib/*/security/pam_secureEye.so usr/lib/*/secureEye -usr/lib/secureeye-authd/wheels -usr/lib/secureeye-authd/requirements.txt usr/lib/systemd/system/secureeye-authd.service usr/share/bash-completion/completions/secureEye +usr/share/man/man1/SecureEye.1 usr/share/secureEye/logo.png etc/secureEye/config.ini -debian/secureeye-authd.sysusers usr/lib/sysusers.d/secureeye-authd.conf - +debian/secureEye.pam-config usr/share/pam-configs/ diff --git a/secureEye/debian/secure-eye.postinst b/secureEye/debian/secure-eye.postinst new file mode 100755 index 0000000..b188460 --- /dev/null +++ b/secureEye/debian/secure-eye.postinst @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +case "$1" in + configure) + if command -v pam-auth-update >/dev/null 2>&1; then + pam-auth-update --package || true + fi + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/secureEye/debian/secure-eye.prerm b/secureEye/debian/secure-eye.prerm new file mode 100755 index 0000000..e4d12bd --- /dev/null +++ b/secureEye/debian/secure-eye.prerm @@ -0,0 +1,14 @@ +#!/bin/sh +set -e + +case "$1" in + remove|purge) + if command -v pam-auth-update >/dev/null 2>&1; then + pam-auth-update --package || true + fi + ;; +esac + +#DEBHELPER# + +exit 0 diff --git a/secureEye/debian/secure-eye.sysusers b/secureEye/debian/secure-eye.sysusers new file mode 100644 index 0000000..8833655 --- /dev/null +++ b/secureEye/debian/secure-eye.sysusers @@ -0,0 +1 @@ +u secureeye - "SecureEye daemon user" /nonexistent diff --git a/secureEye/rpm/README.Fedora.md b/secureEye/rpm/README.Fedora.md index de559e9..85cb817 100644 --- a/secureEye/rpm/README.Fedora.md +++ b/secureEye/rpm/README.Fedora.md @@ -24,16 +24,31 @@ auth sufficient pam_secureEye.so Do NOT edit `/etc/pam.d/system-auth` directly — authselect will overwrite it. -## Runtime venv +## Runtime -`secureeye-authd` builds its recognition virtualenv in -`/usr/lib/secureeye-authd/venv` from bundled wheels during package -installation (`%post`), fully offline. It is rebuilt on every upgrade -and removed on erase. +Nothing is bundled and there is no virtualenv: `secureeye-authd` runs on the +system Python interpreter (`/usr/bin/python3`), so there is nothing to rebuild +after a Python upgrade. ## Backends -- x86_64 / x86_64 ("+v3" Release suffix): mediapipe backend (vendored wheel). -- aarch64: dlib backend. Fedora does not package `python3-dlib`, so a - dlib wheel built from source is vendored into the package and the - shipped `config.ini` defaults to `detector_backend = dlib`. +The recognition backends are ordinary packages from the +[`vhrabar/python-extras`](https://copr.fedorainfracloud.org/coprs/vhrabar/python-extras/) +COPR, selected with `detector_backend` in `/etc/secureEye/config.ini`: + +- `python3-mediapipe` — the default backend, **x86_64 only** +- `python3-dlib` — the alternative backend, and the only one on other + architectures + +Enable that COPR alongside the SecureEye one, or `dnf` cannot resolve the +dependencies: + +```bash +sudo dnf copr enable vhrabar/python-extras +``` + +On non-x86_64 there is no mediapipe, so switch the backend before first use: + +```bash +sudo secureEye config # set detector_backend = dlib +``` diff --git a/secureEye/rpm/secure-eye.spec b/secureEye/rpm/secure-eye.spec index d70ba27..ec68f54 100644 --- a/secureEye/rpm/secure-eye.spec +++ b/secureEye/rpm/secure-eye.spec @@ -8,29 +8,25 @@ # Sources: # Source0 git archive of the tree # Source1 sysusers fragment -# Recognition wheels are downloaded/compiled per-chroot in %%build (needs -# network — enable "internet access" on COPR), so one SRPM serves every -# chroot's Python version/arch. +# +# Nothing is bundled: the recognition backends are ordinary packages from +# copr:vhrabar/python-extras (python3-mediapipe, x86_64 only; python3-dlib) and +# the rest come from Fedora. Add that COPR as an external repository of the +# SecureEye project so the builds and installs can resolve them. -%{!?pkg_version:%global pkg_version 0.1.1} +%{!?pkg_version:%global pkg_version 0.1.2} %undefine __brp_python_bytecompile -%global authd_home %{_prefix}/lib/secureeye-authd - -# dlib recognition backend for non-x86_64 -> temp till python3-dlib is -# ported from ppa/vhrabar -%global dlib_version 20.0.1 - Name: secure-eye Version: %{pkg_version} Release: 1%{?rel_suffix}%{?dist} -Summary: Transitional metapackage for SecureEye split packages +Summary: SecureEye face authentication for Linux License: GPL-2.0-only AND MIT URL: https://github.com/vhrabar/secureEye Source0: %{name}-%{version}.tar.gz -Source1: secureeye-authd.sysusers +Source1: secure-eye.sysusers BuildRequires: meson >= 0.64 BuildRequires: ninja-build @@ -42,62 +38,47 @@ BuildRequires: libevdev-devel BuildRequires: inih-devel BuildRequires: python3-devel BuildRequires: systemd-rpm-macros -# %%build downloads the recognition wheels for this chroot's Python. -BuildRequires: python3-pip - -# Non-x86_64 also compiles the dlib wheel from source in %%build (see below). -%ifnarch x86_64 -BuildRequires: cmake -BuildRequires: make -BuildRequires: python3-setuptools -BuildRequires: python3-wheel -BuildRequires: openblas-devel -BuildRequires: libjpeg-turbo-devel -BuildRequires: libpng-devel -%endif - -Requires: libpam-secureeye = %{version}-%{release} -Requires: secureeye-authd = %{version}-%{release} - -%description -Transitional metapackage that depends on the split SecureEye components -(libpam-secureeye and secureeye-authd). - -%package -n libpam-secureeye -Summary: SecureEye PAM module -%description -n libpam-secureeye -C/C++ PAM module for SecureEye face-authentication integration. -This package contains only PAM-facing components. - -Unlike Debian/Ubuntu there is no pam-auth-update on Fedora; enable the -module manually or via an authselect custom profile (see README.Fedora.md). - -%package -n secureeye-authd -Summary: SecureEye authentication daemon and Python runtime components Requires: python3 >= 3.12 -# %post builds the recognition venv with `python3 -m venv` + pip; on minimal -# Fedora that tooling is not pulled in by python3 alone (deb needs python3-venv). -Requires: python3-pip Requires: python3-numpy Requires: python3-opencv Requires: python3-matplotlib Requires: python3-cffi -Requires: portaudio +# Recognition backends, from copr:vhrabar/python-extras. mediapipe is the +# default backend and is x86_64-only; dlib is the alternative and the only +# backend on other architectures. +%ifarch x86_64 +Requires: python3-mediapipe +%endif +Requires: python3-dlib Requires: v4l-utils -Recommends: libpam-secureeye +# Optional code paths: the ffmpeg recording plugin and the hotkey rubberstamp +# import these lazily and degrade gracefully when they are absent. +Suggests: python3-sounddevice +Suggests: python3-ffmpeg-python +Suggests: python3-keyboard %{?sysusers_requires_compat} %{?systemd_requires} -# Wheels vendored into the runtime venv (see requirements-vendor.txt): -Provides: bundled(python3dist(mediapipe)) -Provides: bundled(python3dist(ffmpeg-python)) -Provides: bundled(python3dist(keyboard)) -%description -n secureeye-authd -Helper daemon and Python runtime stack used by SecureEye face -authentication. Includes systemd service assets and CLI tooling. -The recognition virtualenv is built at install time (%%post) from -wheels bundled in %{authd_home}/wheels — no network access is needed. +# Merged from the former split packages. +Provides: libpam-secureeye = %{version}-%{release} +Provides: secureeye-authd = %{version}-%{release} +Obsoletes: libpam-secureeye < %{version}-%{release} +Obsoletes: secureeye-authd < %{version}-%{release} + +%description +SecureEye authenticates Linux users with their face. This package ships the +complete stack: the C/C++ PAM module, the secureeye-authd authentication +daemon it talks to over a UNIX socket, the Python recognition runtime and the +secureEye command line tool. + +The recognition backend is selected with detector_backend in +/etc/secureEye/config.ini: mediapipe (the default, x86_64 only) or dlib. The +daemon runs on the system Python interpreter, so there is nothing to rebuild +after a Python upgrade. + +Unlike Debian/Ubuntu there is no pam-auth-update on Fedora; enable the module +manually or via an authselect custom profile (see README.Fedora.md). %prep %autosetup -n %{name}-%{version} @@ -115,27 +96,11 @@ meson setup %{_vpath_builddir} . \ --libdir=%{_lib} \ -Dpython.bytecompile=-1 \ -Dinstall_pam_config=false \ + -Dpython_path=%{_bindir}/python3 \ -Dconfig_dir=%{_sysconfdir}/secureEye \ -Duser_models_dir=%{_sysconfdir}/secureEye/models meson compile -C %{_vpath_builddir} -# Populate wheels/ with the recognition runtime wheels matching this chroot's -# Python and arch (installed into the venv by %%post). Downloads from PyPI, so -# the build needs network — enable "internet access" on the COPR project. -rm -rf wheels && mkdir -p wheels -%ifarch x86_64 -python3 -m pip download --only-binary=:all: --no-deps --no-cache-dir \ - --dest wheels -r requirements-vendor.txt -%else -# No mediapipe wheel for this arch; and Fedora/EPEL ship no python3-dlib and -# there is no prebuilt aarch64 dlib wheel, so compile it from source. -grep -iv '^[[:space:]]*mediapipe' requirements-vendor.txt > wheels-reqs.txt -python3 -m pip download --only-binary=:all: --no-deps --no-cache-dir \ - --dest wheels -r wheels-reqs.txt -python3 -m pip wheel --no-deps --no-build-isolation --no-cache-dir \ - --wheel-dir wheels "dlib==%{dlib_version}" -%endif - %install DESTDIR=%{buildroot} meson install -C %{_vpath_builddir} @@ -145,75 +110,28 @@ rm -rf %{buildroot}%{_datadir}/dlib-data install -d -m 0755 %{buildroot}%{_sysconfdir}/secureEye/models # sysusers fragment. -install -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/secureeye-authd.conf - -# Vendored wheels + venv requirements -install -d -m 0755 %{buildroot}%{authd_home}/wheels -%ifarch x86_64 -install -m 0644 requirements-vendor.txt %{buildroot}%{authd_home}/requirements.txt -# skip the aarch64-only dlib wheel here. -for whl in wheels/*.whl; do - case "$(basename "$whl")" in - dlib-*) echo "Skipping $whl on %{_arch}" ;; - *) cp "$whl" %{buildroot}%{authd_home}/wheels/ ;; - esac -done -%else - -grep -iv '^[[:space:]]*mediapipe' requirements-vendor.txt > reqs-filtered.txt -echo 'dlib' >> reqs-filtered.txt -install -m 0644 reqs-filtered.txt %{buildroot}%{authd_home}/requirements.txt -for whl in wheels/*.whl; do - case "$(basename "$whl")" in - mediapipe-*) echo "Skipping $whl on %{_arch}" ;; - *) cp "$whl" %{buildroot}%{authd_home}/wheels/ ;; - esac -done -sed -i 's/^detector_backend = mediapipe/detector_backend = dlib/' \ - %{buildroot}%{_sysconfdir}/secureEye/config.ini -%endif +install -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/secure-eye.conf -%pre -n secureeye-authd -%sysusers_create_compat %{_sysusersdir}/secureeye-authd.conf +%pre +%sysusers_create_compat %{SOURCE1} -%post -n secureeye-authd +%post %systemd_post secureeye-authd.service -# Build (or rebuild on upgrade) the recognition venv from the bundled wheels. -rm -rf %{authd_home}/venv -python3 -m venv --system-site-packages %{authd_home}/venv -# --no-deps: install the vendored wheels as-is -%{authd_home}/venv/bin/pip install --no-index --no-deps --no-cache-dir \ - --find-links %{authd_home}/wheels \ - -r %{authd_home}/requirements.txt - -%preun -n secureeye-authd + +%preun %systemd_preun secureeye-authd.service -%postun -n secureeye-authd +%postun %systemd_postun_with_restart secureeye-authd.service -if [ "$1" -eq 0 ]; then - rm -rf %{authd_home}/venv -fi %files -# metapackage: no files - -%files -n libpam-secureeye %license LICENSE licenses/MIT.txt -%doc NOTICE secureEye/rpm/README.Fedora.md -%{_libdir}/security/pam_secureEye.so - -%files -n secureeye-authd -%license LICENSE licenses/MIT.txt -%doc NOTICE README.md +%doc NOTICE README.md secureEye/rpm/README.Fedora.md %{_bindir}/secureEye +%{_libdir}/security/pam_secureEye.so %{_libdir}/secureEye/ -%dir %{authd_home} -%{authd_home}/wheels/ -%{authd_home}/requirements.txt -%ghost %dir %{authd_home}/venv %{_unitdir}/secureeye-authd.service -%{_sysusersdir}/secureeye-authd.conf +%{_sysusersdir}/secure-eye.conf %{_datadir}/bash-completion/completions/secureEye %dir %{_datadir}/secureEye %{_datadir}/secureEye/logo.png @@ -223,5 +141,12 @@ fi %dir %{_sysconfdir}/secureEye/models %changelog +* Mon Aug 17 2026 Vedran Hrabar - 0.1.2-1 +- Merge libpam-secureeye and secureeye-authd into a single secure-eye package + and drop the transitional metapackage +- Take the recognition backends from copr:vhrabar/python-extras + (python3-mediapipe on x86_64, python3-dlib) instead of bundling wheels +- Drop the install-time virtualenv; the daemon runs on the system python3 + * Thu Jul 02 2026 Vedran Hrabar - 0.1.1-1 - Initial RPM packaging diff --git a/secureEye/debian/secureeye-authd.sysusers b/secureEye/rpm/secure-eye.sysusers similarity index 100% rename from secureEye/debian/secureeye-authd.sysusers rename to secureEye/rpm/secure-eye.sysusers diff --git a/secureEye/rpm/secureeye-authd.sysusers b/secureEye/rpm/secureeye-authd.sysusers deleted file mode 100644 index e02593c..0000000 --- a/secureEye/rpm/secureeye-authd.sysusers +++ /dev/null @@ -1 +0,0 @@ -u secureeye - "SecureEye daemon user" /nonexistent \ No newline at end of file diff --git a/secureEye/src/bin/secureEye.in b/secureEye/src/bin/secureEye.in index 6ffd74a..81d70a8 100644 --- a/secureEye/src/bin/secureEye.in +++ b/secureEye/src/bin/secureEye.in @@ -3,9 +3,4 @@ PYTHONPATH="@py_sources_dir@/secureEye${PYTHONPATH:+:$PYTHONPATH}" export PYTHONPATH -VENV_PY="@authd_python@" -if [ -x "$VENV_PY" ]; then - exec "$VENV_PY" "@script_path@" "$@" -fi - -exec @python_path@ "@script_path@" "$@" \ No newline at end of file +exec @python_path@ "@script_path@" "$@" diff --git a/secureEye/src/meson.build b/secureEye/src/meson.build index 2f2581f..0499e2b 100644 --- a/secureEye/src/meson.build +++ b/secureEye/src/meson.build @@ -126,6 +126,14 @@ install_data('dlib-data/install.sh', 'dlib-data/Readme.md', install_dir: dlibdat install_man('../SecureEye.1') +# Interpreter baked into the systemd unit and the secureEye launcher. Both need +# an absolute path, so honour python_path when it is one (packaging passes +# -Dpython_path=/usr/bin/python3) and fall back to whatever meson found. +runtime_python = paths_dict.get('python_path') +if not runtime_python.startswith('/') + runtime_python = py.full_path() +endif + systemd = dependency('systemd', required : false) systemd_unit_dir = join_paths(get_option('prefix'), 'lib', 'systemd', 'system') if systemd.found() @@ -139,7 +147,7 @@ authd_service = configure_file( input : 'systemd/secureeye-authd.service.in', output : 'secureeye-authd.service', configuration : configuration_data({ - 'python_path' : py.full_path(), + 'python_path' : runtime_python, 'py_sources_dir' : pysourcesrootdir, }), ) @@ -194,11 +202,9 @@ install_data( # endif cli_path = join_paths(pysourcesinstalldir, 'cli.py') -authd_python = get_option('prefix') / 'lib' / 'secureeye-authd' / 'venv' / 'bin' / 'python3' conf_data = configuration_data({ 'script_path': cli_path, - 'python_path': py.full_path(), - 'authd_python': authd_python, + 'python_path': runtime_python, 'py_sources_dir': pysourcesrootdir, }) diff --git a/secureEye/src/systemd/secureeye-authd.service.in b/secureEye/src/systemd/secureeye-authd.service.in index a470310..19b20d2 100644 --- a/secureEye/src/systemd/secureeye-authd.service.in +++ b/secureEye/src/systemd/secureeye-authd.service.in @@ -18,7 +18,7 @@ Environment=XDG_RUNTIME_DIR=/run/secureeye Environment=XDG_CONFIG_HOME=/var/lib/secureeye/.config Environment=XDG_CACHE_HOME=/var/cache/secureeye Environment=MPLCONFIGDIR=/var/cache/secureeye/matplotlib -ExecStart=/usr/lib/secureeye-authd/venv/bin/python3 @py_sources_dir@/secureEye/authd/main.py +ExecStart=@python_path@ @py_sources_dir@/secureEye/authd/main.py Restart=on-failure RestartSec=1