mirror of
https://github.com/caronc/apprise.git
synced 2025-12-15 10:04:06 +08:00
Apprise Build System Modernization (PEP 621 / RPM CI) (#1368)
This commit is contained in:
197
bin/README.md
197
bin/README.md
@@ -1,60 +1,165 @@
|
||||
# Apprise Development Tools
|
||||
# 🛠️ Apprise Development Guide
|
||||
|
||||
# Common Testing
|
||||
This directory just contains some tools that are useful when developing with Apprise. It is presumed that you've set yourself up with a working development environment before using the tools identified here:
|
||||
Welcome! This guide helps you contribute to Apprise with confidence. It outlines
|
||||
how to set up your local environment, run tests, lint your code, and build
|
||||
packages — all using modern tools like [Tox](https://tox.readthedocs.io/) and
|
||||
[Ruff](https://docs.astral.sh/ruff/).
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
Set up your local development environment using Tox:
|
||||
|
||||
```bash
|
||||
# Using pip, setup a working development environment:
|
||||
pip install -r dev-requirements.txt
|
||||
# Install Tox
|
||||
python -m pip install tox
|
||||
```
|
||||
|
||||
The tools are as follows:
|
||||
Tox manages dependencies, linting, testing, and builds — no need to manually
|
||||
install `requirements-dev.txt`.
|
||||
|
||||
- :gear: `apprise`: This effectively acts as the `apprise` tool would once Apprise has been installed into your environment. However `apprise` uses the branch you're working in. So if you added a new Notification service, you can test with it as you would easily. `apprise` takes all the same parameters as the `apprise` tool does.
|
||||
---
|
||||
|
||||
```bash
|
||||
# simply make your code changes to apprise and test it out:
|
||||
./bin/apprise -t title -b body \
|
||||
mailto://user:pass@example.com
|
||||
```
|
||||
## 🧪 Running Tests
|
||||
|
||||
- :gear: `test.sh`: This allows you to just run the unit tests associated with this project. You can optionally specify a _keyword_ as a parameter and the unit tests will specifically focus on a single test. This is useful when you need to debug something and don't want to run the entire fleet of tests each time. e.g:
|
||||
Use the `qa` environment for full testing and plugin coverage:
|
||||
|
||||
```bash
|
||||
# Run all tests:
|
||||
./bin/tests.sh
|
||||
|
||||
# Run just the tests associated with the rest framework:
|
||||
./bin/tests.sh rest
|
||||
|
||||
# Run just the Apprise config related unit tests
|
||||
./bin/tests.sh config
|
||||
```
|
||||
|
||||
- :gear: `checkdone.sh`: This script just runs a lint check against the code to make sure there are no PEP8 issues and additionally runs a full test coverage report. This is what will happen once you check in your code. Nothing can be merged unless these tests pass with 100% coverage. So it's useful to have this handy to run now and then.
|
||||
|
||||
```bash
|
||||
# Perform PEP8 and test coverage check on all code and reports
|
||||
# back. It's called 'checkdone' because it checks to see if you're
|
||||
# actually done with your code commit or not. :)
|
||||
./bin/checkdone.sh
|
||||
```
|
||||
|
||||
You can optionally just update your path to include this `./bin` directory and call the scripts that way as well. Hence:
|
||||
```bash
|
||||
# Update the path to include the bin directory:
|
||||
export PATH="$(pwd)/bin:$PATH"
|
||||
|
||||
# Now you can call the scripts identified above from anywhere...
|
||||
tox -e qa
|
||||
```
|
||||
|
||||
## RPM Testing
|
||||
To focus on specific tests (e.g., email-related):
|
||||
|
||||
Apprise is also packaged for Redhat/Fedora as an RPM. To verify this processs works correctly an additional tool called `build-rpm.sh` is provided. It's best tested using the Docker environments:
|
||||
```bash
|
||||
# To test with el9; do the following:
|
||||
docker-compose run --rm rpmbuild.el9 build-rpm.sh
|
||||
```bash
|
||||
tox -e qa -- -k email
|
||||
```
|
||||
|
||||
# To test with f39; do the following:
|
||||
docker-compose run --rm rpmbuild.f39 build-rpm.sh
|
||||
```
|
||||
To run a minimal dependency test set:
|
||||
|
||||
```bash
|
||||
tox -e minimal
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧹 Linting and Formatting
|
||||
|
||||
Apprise uses [Ruff](https://docs.astral.sh/ruff/) for linting and formatting.
|
||||
This is configured via `pyproject.toml`.
|
||||
|
||||
Run linting:
|
||||
|
||||
```bash
|
||||
tox -e lint
|
||||
```
|
||||
|
||||
Fix formatting automatically (where possible):
|
||||
|
||||
```bash
|
||||
tox -e format
|
||||
```
|
||||
|
||||
> Linting runs automatically on all PRs that touch Python files via GitHub
|
||||
> Actions and will fail builds on violation.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Pre-Commit Check (Recommended)
|
||||
|
||||
Before pushing or creating a PR, validate your work with:
|
||||
|
||||
```bash
|
||||
tox -e lint,qa
|
||||
```
|
||||
|
||||
Or use a combined check shortcut (if defined):
|
||||
|
||||
```bash
|
||||
tox -e checkdone
|
||||
```
|
||||
|
||||
This ensures your changes are linted, tested, and PR-ready.
|
||||
|
||||
---
|
||||
|
||||
## 📨 CLI Testing
|
||||
|
||||
You can run the `apprise` CLI using your local code without installation or run within docker containers:
|
||||
|
||||
```bash
|
||||
# From the root of the repo
|
||||
./bin/apprise -t "Title" -b "Body" mailto://user:pass@example.com
|
||||
```
|
||||
|
||||
Alternatively you can continue to use the `tox` environment:
|
||||
|
||||
|
||||
```bash
|
||||
# syntax tox -e apprise -- [options], e.g.:
|
||||
tox -e apprise -- -vv -b "test body" -t "test title" mailto://credentials
|
||||
```
|
||||
|
||||
Optionally, add the `bin/apprise` to tests your changes
|
||||
|
||||
```bash
|
||||
bin/apprise -vv -b "test body" -t "test title" <schema>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 RPM Build & Verification
|
||||
|
||||
Apprise supports RPM packaging for Fedora and RHEL-based systems. Use Docker
|
||||
to safely test builds:
|
||||
|
||||
```bash
|
||||
# Build RPM for EL9
|
||||
docker-compose run --rm rpmbuild.el9 build-rpm.sh
|
||||
|
||||
# Build RPM for Fedora 42
|
||||
docker-compose run --rm rpmbuild.f42 build-rpm.sh
|
||||
```
|
||||
|
||||
## 📦 Specific Environment Emulation
|
||||
|
||||
You can also emulate your own docker environment and just test/build inside that
|
||||
```bash
|
||||
# Python v3.9 Testing
|
||||
docker-compose run --rm test.py39 bash
|
||||
|
||||
# Python v3.10 Testing
|
||||
docker-compose run --rm test.py310 bash
|
||||
|
||||
# Python v3.11 Testing
|
||||
docker-compose run --rm test.py311 bash
|
||||
|
||||
# Python v3.12 Testing
|
||||
docker-compose run --rm test.py312 bash
|
||||
```
|
||||
Once you've entered one of these environments, you can leverage the following command to work with:
|
||||
|
||||
1. `bin/test.sh`: runs the full test suite (same as `tox -e qa`)
|
||||
1. `bin/apprise`: launches the Apprise CLI using the local build (same as `tox -e apprise`)
|
||||
1. `ruff check . --fix`: auto-formats the codebase (same as `tox -e format`)
|
||||
1. `ruff check .`: performs lint-only validation (same as `tox -e lint`)
|
||||
1. `coverage run --source=apprise -m pytest tests`: manual test execution with coverage
|
||||
|
||||
The only advantage of this route is the overhead associated with each `tox` call is gone (faster responses). Otherwise just utilizing the `tox` commands can sometimes be easier.
|
||||
|
||||
## 🧪 GitHub Actions
|
||||
|
||||
GitHub Actions runs:
|
||||
- ✅ Full test suite with coverage
|
||||
- ✅ Linting (using Ruff)
|
||||
- ✅ Packaging and validation
|
||||
|
||||
Linting **must pass** before PRs can be merged.
|
||||
|
||||
---
|
||||
|
||||
## 🧠 Developer Tips
|
||||
|
||||
- Add new plugins by following [`demo.py`](https://github.com/caronc/apprise/blob/master/apprise/plugins/demo.py) as a template.
|
||||
- Write unit tests under `tests/` using the `AppriseURLTester` pattern.
|
||||
- All new plugins must include test coverage and pass linting.
|
||||
|
||||
@@ -55,8 +55,8 @@ sys.path.insert(
|
||||
sys.path.insert(0, join(getcwd()))
|
||||
|
||||
# Apprise tool now importable
|
||||
from apprise.cli import main # noqa E402
|
||||
import logging # noqa E402
|
||||
from apprise.cli import main
|
||||
import logging
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
111
bin/build-rpm.sh
111
bin/build-rpm.sh
@@ -26,90 +26,57 @@
|
||||
# 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.
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
# Directory where Apprise Source Code can be found
|
||||
APPRISE_DIR="/apprise"
|
||||
# Set Apprise root directory
|
||||
APPRISE_DIR="${APPRISE_DIR:-/apprise}"
|
||||
PYTHON=python3
|
||||
PIP=pip3
|
||||
VENV_CMD="$PYTHON -m venv"
|
||||
TOX="tox -c $APPRISE_DIR/tox.ini"
|
||||
DIST_DIR="${DIST_DIR:-$PWD/dist}"
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
mkenv(){
|
||||
# Prepares RPM Environment
|
||||
cat << _EOF > $HOME/.rpmmacros
|
||||
# macros
|
||||
%_topdir $APPRISE_DIR
|
||||
%_sourcedir %{_topdir}/dist
|
||||
%_specdir %{_topdir}/dist
|
||||
%_rpmdir %{_topdir}/dist/rpm
|
||||
%_srcrpmdir %{_topdir}/dist/rpm
|
||||
%_builddir %{_topdir}/build/rpm
|
||||
_EOF
|
||||
# Prepare our working directories if not already present
|
||||
mkdir -p $APPRISE_DIR/{dist/rpm,build/rpm}
|
||||
return 0
|
||||
}
|
||||
echo "==> Cleaning previous builds"
|
||||
$TOX -e clean --notest
|
||||
|
||||
clean(){
|
||||
# Tidy .pyc files
|
||||
find $APPRISE_DIR -name '*.pyc' -delete &>/dev/null
|
||||
find $APPRISE_DIR -type d -name '__pycache__' -exec rm -rf {} \ &>/dev/null;
|
||||
# Remove previously build details
|
||||
[ -d "$APPRISE_DIR/apprise.egg-info" ] && rm -rf $APPRISE_DIR/apprise.egg-info
|
||||
[ -d "$APPRISE_DIR/build" ] && rm -rf $APPRISE_DIR/build
|
||||
[ -d "$APPRISE_DIR/BUILDROOT" ] && rm -rf $APPRISE_DIR/BUILDROOT
|
||||
}
|
||||
echo "==> Linting RPM spec"
|
||||
rpmlint "$APPRISE_DIR/packaging/redhat/python-apprise.spec"
|
||||
|
||||
build(){
|
||||
# Test spec file for any issues
|
||||
rpmlint "$APPRISE_DIR/packaging/redhat/python-apprise.spec"
|
||||
[ $? -ne 0 ] && echo "RPMLint Failed!" && return 1
|
||||
echo "==> Running tests"
|
||||
$TOX -e py312
|
||||
|
||||
# Prepare RPM Package
|
||||
# Detect our version
|
||||
local VER=$(rpmspec -q --qf "%{version}\n" \
|
||||
"$APPRISE_DIR/packaging/redhat/python-apprise.spec" 2>/dev/null | head -n1)
|
||||
[ -z "$VER" ] && echo "Could not detect Apprise RPM Version" && return 1
|
||||
echo "==> Generating man pages"
|
||||
ronn --roff --organization="Chris Caron <lead2gold@gmail.com>" \
|
||||
"$APPRISE_DIR/packaging/man/apprise.md"
|
||||
|
||||
if [ ! -f "$APPRISE_DIR/dist/apprise-$VER.tar.gz" ]; then
|
||||
# Build Apprise
|
||||
if [ ! -x $HOME/dev/bin/activate ]; then
|
||||
$VENV_CMD $HOME/dev
|
||||
[ $? -ne 0 ] && echo "Could not create Virtual Python Environment" && return 1
|
||||
fi
|
||||
. $HOME/dev/bin/activate
|
||||
$PIP install coverage babel wheel markdown
|
||||
echo "==> Extracting translations"
|
||||
$TOX -e i18n || { echo "Translation extraction failed!" ; exit 1; }
|
||||
|
||||
pushd $APPRISE_DIR
|
||||
# Build Man Page
|
||||
ronn --roff $APPRISE_DIR/packaging/man/apprise.md
|
||||
$PYTHON setup.py extract_messages
|
||||
$PYTHON setup.py sdist
|
||||
echo "==> Compiling translations"
|
||||
$TOX -e compile || { echo "Translation compilation failed!" ; exit 1; }
|
||||
|
||||
# exit from our virtual environment
|
||||
deactivate
|
||||
fi
|
||||
echo "==> Building source distribution"
|
||||
$TOX -e build-sdist || { echo "sdist build failed!" ; exit 1; }
|
||||
|
||||
# Prepare our RPM Source and SPEC dependencies
|
||||
find "$APPRISE_DIR/packaging/man/" -type f -name '*.1' \
|
||||
-exec cp --verbose {} "$APPRISE_DIR/dist" \;
|
||||
find "$APPRISE_DIR/packaging/redhat" -type f -name '*.patch' \
|
||||
-exec cp --verbose {} "$APPRISE_DIR/dist" \;
|
||||
find "$APPRISE_DIR/packaging/redhat" -type f -name '*.spec' \
|
||||
-exec cp --verbose {} "$APPRISE_DIR/dist" \;
|
||||
VERSION=$(rpmspec -q --qf "%{version}\n" "$APPRISE_DIR/packaging/redhat/python-apprise.spec" | head -n1)
|
||||
TARBALL="$APPRISE_DIR/dist/apprise-${VERSION}.tar.gz"
|
||||
|
||||
# Build and Test our RPM Package
|
||||
rpmbuild -ba "$APPRISE_DIR/dist/python-apprise.spec"
|
||||
return $?
|
||||
}
|
||||
if [[ ! -f "$TARBALL" ]]; then
|
||||
echo "Tarball not found: $TARBALL"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prepare our environment
|
||||
mkenv
|
||||
echo "==> Copying tarball to SOURCES directory"
|
||||
mkdir -p "$APPRISE_DIR/SOURCES"
|
||||
cp "$TARBALL" "$APPRISE_DIR/SOURCES/"
|
||||
|
||||
# Clean
|
||||
clean
|
||||
echo "==> Building RPM"
|
||||
rpmbuild --define "_topdir $APPRISE_DIR" \
|
||||
--define "_sourcedir $APPRISE_DIR/SOURCES" \
|
||||
--define "_specdir $APPRISE_DIR/packaging/redhat" \
|
||||
--define "_srcrpmdir $APPRISE_DIR/SRPMS" \
|
||||
--define "_rpmdir $DIST_DIR" \
|
||||
-ba "$APPRISE_DIR/packaging/redhat/python-apprise.spec"
|
||||
|
||||
# Build
|
||||
build
|
||||
echo "✅ RPM build completed successfully"
|
||||
|
||||
# Return our build status
|
||||
exit $?
|
||||
|
||||
105
bin/checkdone.sh
105
bin/checkdone.sh
@@ -1,105 +0,0 @@
|
||||
#!/bin/bash
|
||||
# -*- 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.
|
||||
|
||||
# Absolute path to this script, e.g. /home/user/bin/foo.sh
|
||||
SCRIPT=$(readlink -f "$0")
|
||||
|
||||
# Absolute path this script is in, thus /home/user/bin
|
||||
SCRIPTPATH=$(dirname "$SCRIPT")
|
||||
|
||||
PYTHONPATH=""
|
||||
|
||||
FOUNDROOT=1
|
||||
if [ -f "$(dirname $SCRIPTPATH)/setup.cfg" ]; then
|
||||
pushd "$(dirname $SCRIPTPATH)" &>/dev/null
|
||||
FOUNDROOT=$?
|
||||
PYTHONPATH="$(dirname $SCRIPTPATH)"
|
||||
|
||||
elif [ -f "$SCRIPTPATH/setup.cfg" ]; then
|
||||
pushd "$SCRIPTPATH" &>/dev/null
|
||||
FOUNDROOT=$?
|
||||
PYTHONPATH="$SCRIPTPATH"
|
||||
fi
|
||||
|
||||
if [ $FOUNDROOT -ne 0 ]; then
|
||||
echo "Error: Could not locate apprise setup.cfg file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Tidy previous reports (if present)
|
||||
[ -d .coverage-reports ] && rm -rf .coverage-reports
|
||||
|
||||
# This is a useful tool for checking for any lint errors and additionally
|
||||
# checking the overall coverage.
|
||||
which flake8 &>/dev/null
|
||||
[ $? -ne 0 ] && \
|
||||
echo "Missing flake8; make sure it is installed:" && \
|
||||
echo " > pip install flake8" && \
|
||||
exit 1
|
||||
|
||||
which coverage &>/dev/null
|
||||
[ $? -ne 0 ] && \
|
||||
echo "Missing coverage; make sure it is installed:" &&
|
||||
echo " > pip install pytest-cov coverage" && \
|
||||
exit 1
|
||||
|
||||
echo "Performing PEP8 check..."
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH flake8 . --show-source --statistics
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "PEP8 check failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "PEP8 check succeeded; no errors found! :)"
|
||||
echo
|
||||
|
||||
# Run our unit test coverage check
|
||||
echo "Running test coverage check..."
|
||||
pushd $PYTHONPATH &>/dev/null
|
||||
if [ ! -z "$@" ]; then
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH coverage run -m pytest -vv -k "$@"
|
||||
RET=$?
|
||||
|
||||
else
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH coverage run -m pytest -vv
|
||||
RET=$?
|
||||
fi
|
||||
|
||||
if [ $RET -ne 0 ]; then
|
||||
echo "Tests failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Build our report
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH coverage combine
|
||||
|
||||
# Prepare XML Reference
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH coverage xml
|
||||
|
||||
# Print our report
|
||||
LANG=C.UTF-8 PYTHONPATH=$PYTHONPATH coverage report --show-missing
|
||||
@@ -45,14 +45,14 @@ SCRIPTPATH=$(dirname "$SCRIPT")
|
||||
|
||||
PYTHONPATH=""
|
||||
|
||||
if [ -f "$(dirname $SCRIPTPATH)/setup.cfg" ]; then
|
||||
if [ -f "$(dirname $SCRIPTPATH)/pyproject.toml" ]; then
|
||||
PYTHONPATH="$(dirname $SCRIPTPATH)"
|
||||
|
||||
elif [ -f "$SCRIPTPATH/setup.cfg" ]; then
|
||||
elif [ -f "$SCRIPTPATH/pyproject.toml" ]; then
|
||||
PYTHONPATH="$SCRIPTPATH"
|
||||
|
||||
else
|
||||
echo "Error: Could not locate apprise setup.cfg file."
|
||||
echo "Error: Could not locate apprise pyproject.toml file."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
Reference in New Issue
Block a user