Skip to content

Commit

Permalink
Merge pull request #1 from AlphaJack/notifications
Browse files Browse the repository at this point in the history
Apprise Notifications
  • Loading branch information
AlphaJack authored Nov 21, 2024
2 parents e049d70 + 580241d commit e39fc89
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/from_tag_to_build_release_pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ jobs:
steps:

- name: Wait for the new release to be accessible from PyPI
run: sleep 30
run: sleep 10

- name: Update PKGBUILD
uses: aksh1618/update-aur-package@93d86c3ea6e57fb485881f5ce8b15375dcd23918 # 1.0.5
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
.venv
build
dist
*egg-info
**/**.pyc
Expand Down
35 changes: 27 additions & 8 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,47 @@
# git push --delete origin v1.0.0
# and repeat the steps below

tests:
bash tests/tests.sh
install:
rm -rf .venv
python -m venv .venv
.venv/bin/pip install -e ".[dev]"

release:
mypy --check-untyped-defs .
lint:
mypy --check-untyped-defs --ignore-missing-imports .
ruff check --fix .

format:
ruff format .
find * -type f -exec toc -f {} \;
git status

test:
bash tests/tests.sh

toc:
find * -type f ! -name 'CHANGELOG.md' -exec toc -f {} \;

review:
git status
echo "Abort now if there are files that needs to be committed"
sleep 10

tag_bump:
grep -q $(tag) pyproject.toml || sed -i pyproject.toml -e "s|version = .*|version = \"$(tag)\"|"

tag_changelog:
git tag v$(tag) -m v$(tag)
# enter "v1.0.0"
git-cliff -c pyproject.toml > CHANGELOG.md
#toc -lf .tocfiles

tag_commit_new_changelog:
git tag --delete v$(tag)
git add pyproject.toml || true
git add CHANGELOG.md || true
git commit -m "minor: updated CHANGELOG.md" || true
git tag -fa v$(tag) -m v$(tag)

tag_publish::
git push --follow-tags
echo "Remember to update the AUR package"

tag: tag_bump tag_changelog tag_commit_new_changelog tag_publish

release: lint format test toc review tag
25 changes: 23 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@

# Rusticlone

<img alt="PyPI Downloads" src="https://static.pepy.tech/badge/rusticlone">
<img alt="Test Coverage" src="https://github.com/AlphaJack/rusticlone/raw/master/images/coverage.svg">

<p style='text-align: center;'>
<strong>3-2-1 backups using Rustic and RClone</strong>
</p>
Expand Down Expand Up @@ -192,6 +195,21 @@ rusticlone -r "gdrive:/PC" download
rusticlone extract
```

### Push notifications

Rusticlone can send a push notification with the operation results using Apprise:

<img alt="Push Notification" src="https://github.com/AlphaJack/rusticlone/raw/master/images/notification.png">

Just pass the [Apprise notification URL](https://github.com/caronc/apprise?tab=readme-ov-file#supported-notifications) via the `--apprise-url` argument or `APPRISE_URL` environment variable:

```bash
rusticlone --apprise-url "tgram:/XXXXXX/YYYYYY/" archive

#alternative
APPRISE_URL="tgram:/XXXXXX/YYYYYY/" rusticlone archive
```

### Parallel processing

You can specify the `--parallel` argument with any command to process all your profiles at the same time:
Expand All @@ -202,6 +220,8 @@ rusticlone --parallel -r "gdrive:/PC" backup

Beware that this may fill your RAM if you have many profiles or several GB of data to archive.

Parallel processing is also not (yet) compatible with push notifications.

### Exclude profiles

Rustic has a handy feature: you can create additional profiles to store options shared between profiles.
Expand Down Expand Up @@ -290,10 +310,11 @@ sudo systemctl enable --now rusticlone.timer
You can test Rusticlone with dummy files before using it for your precious data:

```bash
bash tests/tests.sh
make install
make test
```

You will need `bash`, `coreutils`, `python-coverage`, `rclone`, and `rustic` installed to run the test.
You will need `bash`, `coreutils`, `rclone`, and `rustic` installed to run the test.
Before running the test, make sure that you have no important files under "$HOME/.config/rustic".

At the end, you can read a test coverage report with your browser, to see which lines of the source code were run during the test.
Expand Down
1 change: 1 addition & 0 deletions images/coverage.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/notification.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,12 @@ classifiers = [
"Programming Language :: Python :: 3"
]
dependencies = [
"importlib_metadata"
"importlib_metadata",
"ConfigArgParse"
]
[project.optional-dependencies]
dev = ["mypy", "ruff", "git-cliff", "coverage", "genbadge[coverage]", "tableofcontents"]
full = ["apprise"]

[project.scripts]
rusticlone = "rusticlone.cli:main"
Expand Down
31 changes: 26 additions & 5 deletions rusticlone/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
# ################################################################ IMPORTS

# accept arguments
import argparse
import configargparse

# version
# from importlib_metadata import version
Expand All @@ -33,7 +33,7 @@ def parse_args():
"""
Parse the command-line arguments and return the parsed arguments
"""
parser = argparse.ArgumentParser(
parser = configargparse.ArgumentParser(
prog="rusticlone",
description="3-2-1 backups using Rustic and RClone",
)
Expand All @@ -44,29 +44,50 @@ def parse_args():
nargs=1,
choices=("archive", "upload", "backup", "download", "extract", "restore"),
)
parser.add_argument(
"-a",
"--apprise-url",
type=str,
env_var="APPRISE_URL",
help="Apprise URL for notification",
)
parser.add_argument(
"-i",
"--ignore",
type=str,
default="🫣🫣🫣",
env_var="IGNORE",
help="Ignore rustic profiles containing this pattern",
)
parser.add_argument(
"-l", "--log-file", type=str, help="Log file for Rustic and RClone"
"-l",
"--log-file",
type=str,
env_var="LOG_FILE",
help="Log file for Rustic and RClone",
)
parser.add_argument(
"-p", "--parallel", action="store_true", help="Process profiles in parallel"
"-p",
"--parallel",
action="store_true",
env_var="PARALLEL",
help="Process profiles in parallel",
)
group = parser.add_mutually_exclusive_group()
group.add_argument(
"-P",
"--profile",
type=str,
default="*",
env_var="RUSTIC_PROFILE",
help="Individual Rustic profile to process",
)
parser.add_argument(
"-r", "--remote", type=str, help="RClone remote and subdirectory"
"-r",
"--remote",
type=str,
env_var="RCLONE_REMOTE",
help="RClone remote and subdirectory",
)
# parser.add_argument(
# "-v",
Expand Down
31 changes: 24 additions & 7 deletions rusticlone/helpers/custom.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pathlib import Path

# args type
from argparse import Namespace
from configargparse import Namespace

# os and hostname
import platform
Expand All @@ -28,6 +28,7 @@

# rusticlone
from rusticlone.helpers.action import Action
from rusticlone.helpers.notification import notify_user
from rusticlone.processing.parallel import (
system_backup_parallel,
system_archive_parallel,
Expand Down Expand Up @@ -62,6 +63,7 @@ def __init__(self, args) -> None:
self.command = args.command[0]
self.operating_system = platform.system()
self.default_log_file = Path("rusticlone.log")
self.apprise_url = ""
# log file
# rustic use log file from config, while from rclone it is passed from either cli or here
if args.log_file is not None:
Expand Down Expand Up @@ -91,6 +93,8 @@ def __init__(self, args) -> None:
self.provided_profile = args.profile
else:
self.provided_profile = ""
if args.apprise_url:
self.apprise_url = args.apprise_url

def check_log_file(self) -> None:
"""
Expand Down Expand Up @@ -172,10 +176,12 @@ def process_profiles(
command: str,
log_file: Path,
remote_prefix: str,
apprise_url: str,
) -> None:
"""
Process all profiles according to the command specified and parallel flag
"""
results = {}
if parallel:
match command:
case "backup":
Expand All @@ -200,30 +206,40 @@ def process_profiles(
)
case "extract":
system_extract_parallel(profiles=profiles, log_file=log_file)
case _:
print(f"Invalid command '{command}'")
else:
match command:
case "backup":
system_backup_sequential(
results = system_backup_sequential(
profiles=profiles, log_file=log_file, remote_prefix=remote_prefix
)
case "archive":
system_archive_sequential(profiles=profiles, log_file=log_file)
results = system_archive_sequential(
profiles=profiles, log_file=log_file
)
case "upload":
system_upload_sequential(
results = system_upload_sequential(
profiles=profiles, log_file=log_file, remote_prefix=remote_prefix
)
case "restore":
system_restore_sequential(
results = system_restore_sequential(
profiles=profiles,
log_file=log_file,
remote_prefix=remote_prefix,
)
case "download":
system_download_sequential(
results = system_download_sequential(
profiles=profiles, log_file=log_file, remote_prefix=remote_prefix
)
case "extract":
system_extract_sequential(profiles=profiles, log_file=log_file)
results = system_extract_sequential(
profiles=profiles, log_file=log_file
)
case _:
print(f"Invalid command '{command}'")
if apprise_url and results:
notify_user(results, apprise_url)


def load_customizations(args: Namespace):
Expand All @@ -242,4 +258,5 @@ def load_customizations(args: Namespace):
custom.command,
custom.log_file,
custom.remote_prefix,
custom.apprise_url,
)
Loading

0 comments on commit e39fc89

Please sign in to comment.