EPEL10 GitHub workflow added - RPM Building (#1460)

This commit is contained in:
Chris Caron
2025-12-01 21:29:08 -05:00
committed by GitHub
parent 890e24a1ea
commit 4109cf105d
13 changed files with 250 additions and 106 deletions

View File

@@ -1,5 +1,5 @@
#
# Verify on CI/GHA that package building works.
# Verify on CI/GHA that RPM package building works.
#
name: RPM Packaging
@@ -13,11 +13,16 @@ on:
jobs:
build:
name: Build RPMs
name: Build RPMs (${{ matrix.dist }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dist: [el9, el10]
container:
image: ghcr.io/caronc/apprise-rpmbuild:el9
image: ghcr.io/caronc/apprise-rpmbuild:${{ matrix.dist }}
options: --user root
steps:
@@ -28,46 +33,77 @@ jobs:
run: ./bin/build-rpm.sh
env:
APPRISE_DIR: ${{ github.workspace }}
# Drop RPMs into dist/<dist> inside the workspace
DIST_DIR: ${{ github.workspace }}/dist/${{ matrix.dist }}
- name: Show RPMs found for upload
run: |
echo "Listing dist/**/*.rpm:"
find dist -type f -name '*.rpm'
echo "Listing dist/${{ matrix.dist }}/**/*.rpm:"
find dist/${{ matrix.dist }} -type f -name '*.rpm'
- name: Upload RPM Artifacts
uses: actions/upload-artifact@v4
with:
name: built-rpms
path: |
dist/rpm/*/*.rpm
name: built-rpms-${{ matrix.dist }}
path: ./dist/${{ matrix.dist }}
if-no-files-found: error
retention-days: 5
- name: Upload rpmlint config files
uses: actions/upload-artifact@v4
with:
# Upload the specific files needed for verification
name: rpmlint-config-${{ matrix.dist }}
path: ./packaging/redhat/python-apprise.rpmlintrc.${{ matrix.dist }}
retention-days: 5
verify:
name: Verify RPMs
name: Verify RPMs (${{ matrix.dist }})
needs: build
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
dist: [el9, el10]
container:
image: ghcr.io/caronc/apprise-rpmbuild:el9
image: ghcr.io/caronc/apprise-rpmbuild:${{ matrix.dist }}
options: --user root
steps:
- name: Download built RPMs
uses: actions/download-artifact@v4
with:
name: built-rpms
name: built-rpms-${{ matrix.dist }}
path: ./dist
- name: Download rpmlint config files
uses: actions/download-artifact@v4
with:
name: rpmlint-config-${{ matrix.dist }}
# Download files directly into the correct directory
path: ./packaging/redhat
- name: Lint RPMs
run: rpmlint ./dist/**/*.rpm
run: |
set -e
RC_FILE="./packaging/redhat/python-apprise.rpmlintrc.${{ matrix.dist }}"
if rpmlint --help 2>&1 | grep -q -- '--rpmlintrc'; then
echo "Using rpmlint --rpmlintrc with $RC_FILE"
rpmlint --rpmlintrc "$RC_FILE" ./dist/**/*.rpm
else
echo "Using rpmlint v1.x on older distribution"
rpmlint -f "$RC_FILE" ./dist/**/*.rpm
fi
- name: Install and verify RPMs
run: |
echo "Installing RPMs from: ./dist/"
find ./dist -name '*.rpm'
dnf install -y ./dist/**/*.rpm
apprise --version
- name: Check Installed Files
run: rpm -qlp ./dist/**/*.rpm

View File

@@ -115,10 +115,13 @@ to safely test builds:
```bash
# Build RPM for EL9
docker-compose run --rm rpmbuild.el9 build-rpm.sh
docker-compose run --rm rpmbuild.el9 /apprise/bin/build-rpm.sh
# Build RPM for EL10
docker-compose run --rm rpmbuild.el10 /apprise/bin/build-rpm.sh
# Build RPM for Fedora 42
docker-compose run --rm rpmbuild.f42 build-rpm.sh
docker-compose run --rm rpmbuild.f42 /apprise/bin/build-rpm.sh
```
## 📦 Specific Environment Emulation

View File

@@ -43,7 +43,7 @@ if [ ! -d "$DIST_DIR" ]; then
$TOX -e clean --notest
echo "==> Linting RPM spec"
rpmlint "$APPRISE_DIR/packaging/redhat/python-apprise.spec"
rpmlint "$APPRISE_DIR/packaging/redhat/python-apprise.spec"
echo "==> Generating man pages"
ronn --roff --organization="Chris Caron <lead2gold@gmail.com>" \

View File

@@ -44,6 +44,15 @@ services:
working_dir: /apprise
user: "1000:1000"
rpmbuild.el10:
build:
context: .
dockerfile: tests/docker/Dockerfile.el10
volumes:
- ./:/apprise
working_dir: /apprise
user: "1000:1000"
rpmbuild.f42:
build:
context: .
@@ -83,6 +92,8 @@ services:
#
# el9
# - docker-compose run --rm rpmbuild.el9 build-rpm.sh
# - docker-compose run --rm rpmbuild.el9 /apprise/bin/build-rpm.sh
# el10
# - docker-compose run --rm rpmbuild.el10 /apprise/bin/build-rpm.sh
# f42 (Fedora)
# - docker-compose run --rm rpmbuild.f42 build-rpm.sh
# - docker-compose run --rm rpmbuild.f42 /apprise/bin/build-rpm.sh

View File

@@ -5,11 +5,11 @@ Let me know if you'd like to help me host on more platforms or can offer to do i
### RPM Based Packages
* [EPEL](https://fedoraproject.org/wiki/EPEL) based distributions are only supported if they are of v9 or higher. This includes:
* Red Hat 9.x (or higher)
* Scientific OS 9.x (or higher)
* Oracle Linux 9.x (or higher)
* Rocky Linux 9.x (or higher)
* Alma Linux 9.x (or higher)
* Red Hat 10.x (or higher)
* Scientific OS 10.x (or higher)
* Oracle Linux 10.x (or higher)
* Rocky Linux 10.x (or higher)
* Alma Linux 110.x (or higher)
* Fedora 29 (or higher)
Provided you are connected to the [EPEL repositories](https://fedoraproject.org/wiki/EPEL), the following will just work for you:
@@ -21,7 +21,10 @@ dnf install python3-apprise apprise
You can build your own rpm packges with the following:
```bash
# EPEL9 (CentOS/Rocky/RedHat/Oracle Linux)
# EPEL10 (Rocky/RedHat/Oracle Linux)
tox -e build-el10-rpm
# EPEL9 (Rocky/RedHat/Oracle Linux)
tox -e build-el9-rpm
# Fedora 42

View File

@@ -0,0 +1,4 @@
from Config import *
# Ignore false-positive spelling errors on brand names
addFilter('spelling-error.*ntfy')

View File

@@ -0,0 +1,7 @@
from Config import *
# Ignore false-positive spelling errors on brand names
addFilter('spelling-error.*ntfy')
addFilter('spelling-error.*httpSMS')
addFilter('invalid-license BSD-2-Clause')
addFilter('obsolete-not-provided python.*-apprise')

View File

@@ -47,9 +47,11 @@
# - Prevent warnings:
# en_US ntfy -> notify
# en_US httpSMS -> HTTP
#
# rpmlint: ignore-spelling httpSMS ntfy
# - RHEL9 does not recognize: BSD-2-Clause which is correct
#
# rpmlint: ignore invalid-license
%global common_description %{expand: \
@@ -87,15 +89,15 @@ URL: https://github.com/caronc/%{pypi_name}
Source0: %{url}/archive/v%{version}/%{pypi_name}-%{version}.tar.gz
BuildArch: noarch
Obsoletes: python%{python3_pkgversion}-%{pypi_name} < %{version}-%{release}
Provides: python%{python3_pkgversion}-%{pypi_name} = %{version}-%{release}
%description %{common_description}
%package -n %{pypi_name}
Summary: Notify messaging platforms from the command line
Requires: python%{python3_pkgversion}-click >= 5.0
Obsoletes: %{pypi_name} < %{version}-%{release}
Provides: %{pypi_name} = %{version}-%{release}
Requires: python3dist(click) >= 5.0
Requires: python%{python3_pkgversion}-%{pypi_name} = %{version}-%{release}
%description -n %{pypi_name}
@@ -105,39 +107,44 @@ services.
%package -n python%{python3_pkgversion}-%{pypi_name}
Summary: A simple wrapper to many popular notification services used today
Obsoletes: python%{python3_pkgversion}-%{pypi_name} < %{version}-%{release}
%{?python_provide:%python_provide python%{python3_pkgversion}-%{pypi_name}}
BuildRequires: gettext
BuildRequires: python%{python3_pkgversion}-devel
%if %{legacy_python_build}
# backwards compatible
BuildRequires: python%{python3_pkgversion}-setuptools
BuildRequires: python3dist(setuptools)
%endif
BuildRequires: python%{python3_pkgversion}-wheel
BuildRequires: python%{python3_pkgversion}-requests
BuildRequires: python%{python3_pkgversion}-requests-oauthlib
BuildRequires: python%{python3_pkgversion}-click >= 5.0
BuildRequires: python%{python3_pkgversion}-markdown
BuildRequires: python%{python3_pkgversion}-yaml
BuildRequires: python%{python3_pkgversion}-babel
BuildRequires: python%{python3_pkgversion}-cryptography
BuildRequires: python%{python3_pkgversion}-certifi
BuildRequires: python%{python3_pkgversion}-tox
Requires: python%{python3_pkgversion}-requests
Requires: python%{python3_pkgversion}-requests-oauthlib
Requires: python%{python3_pkgversion}-markdown
Requires: python%{python3_pkgversion}-cryptography
Requires: python%{python3_pkgversion}-certifi
Requires: python%{python3_pkgversion}-yaml
Recommends: python%{python3_pkgversion}-paho-mqtt
BuildRequires: python3dist(wheel)
BuildRequires: python3dist(requests)
BuildRequires: python3dist(requests-oauthlib)
BuildRequires: python3dist(click) >= 5.0
BuildRequires: python3dist(markdown)
BuildRequires: python3dist(pyyaml)
BuildRequires: python3dist(babel)
BuildRequires: python3dist(cryptography)
BuildRequires: python3dist(certifi)
BuildRequires: python3dist(tox)
%if %{with tests}
BuildRequires: python%{python3_pkgversion}-pytest
BuildRequires: python%{python3_pkgversion}-pytest-mock
BuildRequires: python%{python3_pkgversion}-pytest-runner
BuildRequires: python%{python3_pkgversion}-pytest-cov
BuildRequires: python3dist(pytest)
BuildRequires: python3dist(pytest-mock)
BuildRequires: python3dist(pytest-runner)
BuildRequires: python3dist(pytest-cov)
%endif
Requires: python3dist(requests)
Requires: python3dist(requests-oauthlib)
Requires: python3dist(markdown)
Requires: python3dist(cryptography)
Requires: python3dist(certifi)
Requires: python3dist(pyyaml)
Recommends: python3dist(paho-mqtt)
%if 0%{?legacy_python_build} == 0
# Logic for non-RHEL ≤ 9 systems
%generate_buildrequires
@@ -203,18 +210,16 @@ LANG=C.UTF-8 PYTHONPATH=%{buildroot}%{python3_sitelib}:%{_builddir}/%{name}-%{ve
%{python3_sitelib}/%{pypi_name}/
# Exclude i18n as it is handled below with the lang(spoken) tag below
%exclude %{python3_sitelib}/%{pypi_name}/cli.*
# Handle egg-info to dist-info transfer
%if 0%{?fedora} >= 40 || 0%{?rhel} >= 10
%{python3_sitelib}/apprise-*.dist-info/
%else
%{python3_sitelib}/apprise-*.egg-info
%endif
%exclude %{python3_sitelib}/%{pypi_name}/__pycache__/cli*.py?
%if %{legacy_python_build}
# Handle egg-info vs. dist-info based on build backend
%{python3_sitelib}/apprise-*.egg-info
# Legacy: include all compiled locales that we produced under the package tree
%{python3_sitelib}/%{pypi_name}/i18n/*/LC_MESSAGES/apprise.mo
%lang(en) %{python3_sitelib}/%{pypi_name}/i18n/en/LC_MESSAGES/apprise.mo
%else
# Handle egg-info vs. dist-info based on build backend
%{python3_sitelib}/apprise-*.dist-info/
# Localised Files
%exclude %{python3_sitelib}/%{pypi_name}/i18n/
%lang(en) %{python3_sitelib}/%{pypi_name}/i18n/en/LC_MESSAGES/apprise.mo
@@ -223,7 +228,7 @@ LANG=C.UTF-8 PYTHONPATH=%{buildroot}%{python3_sitelib}:%{_builddir}/%{name}-%{ve
%files -n %{pypi_name}
%{_bindir}/%{pypi_name}
%{_mandir}/man1/%{pypi_name}.1*
%{python3_sitelib}/%{pypi_name}/cli.*
%{python3_sitelib}/%{pypi_name}/cli.py
%{python3_sitelib}/%{pypi_name}/__pycache__/cli*.py?
%changelog

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
# BSD 2-Clause License
#
# Apprise - Push Notification Library.
# Copyright (c) 2025, Chris Caron <lead2gold@gmail.com>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# Base
FROM rockylinux/rockylinux:10
ENV container=docker
# Basic build tooling and RPM stack
RUN \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/yum.conf; \
dnf -y update && \
dnf install -y epel-release; \
dnf install -y rpm-build python3-pip \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools && \
dnf config-manager --set-enabled crb && \
dnf -y install pyproject-rpm-macros rpmlint rubygem-ronn-ng && \
dnf clean all
# Place our build file into the path
COPY packaging/redhat/python-apprise.spec /
RUN rpmspec -q --buildrequires /python-apprise.spec | cut -f1 -d' ' | \
xargs dnf install -y && dnf clean all
# RPM build structure setup
ENV FLAVOR=rpmbuild OS=centos DIST=el10
RUN useradd builder -u 1000 -m -G users,wheel &>/dev/null && \
echo "builder ALL=(ALL:ALL) NOPASSWD:ALL" >> /etc/sudoers
VOLUME ["/apprise"]
WORKDIR /apprise
# RPMs should never be built as root
USER builder

View File

@@ -27,25 +27,27 @@
# POSSIBILITY OF SUCH DAMAGE.
# Base
FROM rockylinux:9
FROM rockylinux/rockylinux:9
ENV container=docker
# Basic build tooling and RPM stack
RUN \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/yum.conf; \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools; \
dnf config-manager --set-enabled crb
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/yum.conf; \
dnf -y update && \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools; \
dnf config-manager --set-enabled crb
# Place our build file into the path
COPY bin/build-rpm.sh /usr/bin
COPY packaging/redhat/python-apprise.spec /
RUN rpmspec -q --buildrequires /python-apprise.spec | cut -f1 -d' ' | \
xargs dnf install -y && dnf clean all

View File

@@ -45,21 +45,20 @@ RUN dnf download -y --destdir BZ2216807 --resolve dnf-data && \
rpm -Uhi --force BZ2216807/*
RUN \
rm -f /usr/lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /usr/lib/systemd/system/local-fs.target.wants/*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /usr/lib/systemd/system/basic.target.wants/*;\
rm -f /usr/lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/dnf/dnf.conf; \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools
rm -f /usr/lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /usr/lib/systemd/system/local-fs.target.wants/*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /usr/lib/systemd/system/basic.target.wants/*;\
rm -f /usr/lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/dnf/dnf.conf; \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools
# Place our build file into the path
COPY bin/build-rpm.sh /usr/bin
COPY packaging/redhat/python-apprise.spec /
RUN rpmspec -q --buildrequires /python-apprise.spec | cut -f1 -d' ' | \
xargs dnf install -y && dnf clean all

View File

@@ -45,21 +45,20 @@ RUN dnf download -y --destdir BZ2216807 --resolve dnf-data && \
rpm -Uhi --force BZ2216807/*
RUN \
rm -f /usr/lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /usr/lib/systemd/system/local-fs.target.wants/*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /usr/lib/systemd/system/basic.target.wants/*;\
rm -f /usr/lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/dnf/dnf.conf; \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools
rm -f /usr/lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /usr/lib/systemd/system/local-fs.target.wants/*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /usr/lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /usr/lib/systemd/system/basic.target.wants/*;\
rm -f /usr/lib/systemd/system/anaconda.target.wants/*; \
echo "assumeyes=1" >> /etc/dnf/dnf.conf; \
dnf install -y epel-release; \
dnf install -y rpm-build rpmlint python3-pip rubygem-ronn \
dnf-plugins-core 'dnf-command(config-manager)' \
'dnf-command(builddep)' sudo rsync rpmdevtools
# Place our build file into the path
COPY bin/build-rpm.sh /usr/bin
COPY packaging/redhat/python-apprise.spec /
RUN rpmspec -q --buildrequires /python-apprise.spec | cut -f1 -d' ' | \
xargs dnf install -y && dnf clean all

16
tox.ini
View File

@@ -57,7 +57,15 @@ skip_install = true
allowlist_externals =
docker
commands =
docker compose run --rm rpmbuild.el9 bash /usr/bin/build-rpm.sh
docker compose run --rm rpmbuild.el9 bash /apprise/bin/build-rpm.sh
[testenv:build-el10-rpm]
description = Run RPM packaging for EPEL10 via Docker
skip_install = true
allowlist_externals =
docker
commands =
docker compose run --rm rpmbuild.el10 bash /apprise/bin/build-rpm.sh
[testenv:build-f42-rpm]
description = Run RPM packaging for Fedora 42 via Docker
@@ -65,7 +73,7 @@ skip_install = true
allowlist_externals =
docker
commands =
docker compose run --rm rpmbuild.f42 bash /usr/bin/build-rpm.sh
docker compose run --rm rpmbuild.f42 bash /apprise/bin/build-rpm.sh
[testenv:build-rawhide-rpm]
description = Run RPM packaging for Fedora Rawhide via Docker
@@ -73,7 +81,7 @@ skip_install = true
allowlist_externals =
docker
commands =
docker compose run --rm rpmbuild.rawhide bash /usr/bin/build-rpm.sh
docker compose run --rm rpmbuild.rawhide bash /apprise/bin/build-rpm.sh
[testenv:lint]
description = Run static analysis using Ruff
@@ -204,7 +212,7 @@ commands =
description = Rebuild the Apprise man page
allowlist_externals = docker
commands =
docker compose run --rm rpmbuild.el9 ronn \
docker compose run --rm rpmbuild.el10 ronn \
--organization="Chris Caron <lead2gold@gmail.com>" \
packaging/man/apprise.md