Compare commits

..

3 commits

Author SHA1 Message Date
cd1c8c4e91 style: log S3 destination path when archive is uploaded instead of local path (#4)
Reviewed-on: https://codeberg.org/vlw/3rd/pulls/4
2026-01-01 13:35:49 +01:00
vlw
f317c41a67 docs: update README wording of config file structure (#2)
Reviewed-on: https://codeberg.org/vlw/3rd/pulls/2
Co-authored-by: vlw <victor@vlw.se>
Co-committed-by: vlw <victor@vlw.se>
2026-01-01 13:35:26 +01:00
234e125d84 fix: resolved runtime issues caused by subroutines expecting old config format (#3)
This PR fixes some issues with invoking the Archive class when running the script.

Reviewed-on: https://codeberg.org/vlw/3rd/pulls/3
2026-01-01 13:35:08 +01:00
8 changed files with 36 additions and 18 deletions

2
.gitignore vendored
View file

@ -1,3 +1,3 @@
.example.config.json .config.json
__pycache__ __pycache__

View file

@ -43,7 +43,9 @@ Schedule this backup script to run with a crontab entry, for example:
Which will run at 2:30 each Wednesday. Which will run at 2:30 each Wednesday.
# Config # Config
The config file `.config.json` is used to define parameters and which directories to archive (in autorun mode). The config file (`.config.json` by default) is used to define which directories to archive with parameters.
Directories are specified as an array of objects, each object has key value properties with the following format:
```json ```json
[ [

12
run.py
View file

@ -1,3 +1,4 @@
import os
import typing import typing
import argparse import argparse
@ -9,7 +10,7 @@ from src.Enums import StdoutLevel, Namespace
stdout = Stdout(Namespace.CLI) stdout = Stdout(Namespace.CLI)
def main(file: str) -> None: def main() -> None:
""" """
Autorun from a config file Autorun from a config file
@ -20,7 +21,7 @@ def main(file: str) -> None:
parser = argparse.ArgumentParser(description="Testing") parser = argparse.ArgumentParser(description="Testing")
parser.add_argument("-s", "--sleep", type=int, help="Global log sleep level") parser.add_argument("-s", "--sleep", type=int, help="Global log sleep level")
parser.add_argument("-i", "--input", action="store_true", help="Load config file from path") parser.add_argument("-i", "--input", help="Load config file from path",default=".config.json")
parser.add_argument("-d", "--dryrun", action="store_true", help="Dry run") parser.add_argument("-d", "--dryrun", action="store_true", help="Dry run")
parser.add_argument("-l", "--log-level", type=str, help="Global log level") parser.add_argument("-l", "--log-level", type=str, help="Global log level")
@ -42,12 +43,17 @@ def main(file: str) -> None:
# Set enable dry run # Set enable dry run
if args.dryrun: if args.dryrun:
Aws.dry_run = True Aws.dry_run = True
Archive.preserve_archives = True
stdout.ok("Dry run enabled") stdout.ok("Dry run enabled")
stdout.log("Starting...") stdout.log("Starting...")
if not os.path.isfile(args.input):
stdout.error(f"No config file found at path: '{args.input}'")
exit(1)
for item in Config.from_json_file(args.input): for item in Config.from_json_file(args.input):
Archive(item) Aws(Archive(item)).upload()
stdout.log("Finished!") stdout.log("Finished!")

View file

@ -6,10 +6,12 @@ import subprocess
from ..Cli import Cli from ..Cli import Cli
from ..Stdout import Stdout from ..Stdout import Stdout
from ..Config import Config from ..Config import Config
from .Filesystem import PATH_MANIFEST, Filesystem from .Filesystem import Filesystem
from ..Enums import Namespace, Format, StdoutLevel from ..Enums import Namespace, Format, StdoutLevel
class Archive(): class Archive():
preserve_archives = False
def __init__(self, item: Config): def __init__(self, item: Config):
""" """
Create a new Archive instance for a target item Create a new Archive instance for a target item
@ -46,6 +48,9 @@ class Archive():
Remove archive file Remove archive file
""" """
if Archive.preserve_archives:
return
os.remove(self.output_path) os.remove(self.output_path)
self.__stdout.info(f"Archive removed: {self.output_path}") self.__stdout.info(f"Archive removed: {self.output_path}")
@ -56,6 +61,8 @@ class Archive():
self.__stdout.warn(f"Archiving skipped for: {self.item.abspath_target}") self.__stdout.warn(f"Archiving skipped for: {self.item.abspath_target}")
self.cleanup()
def __compress(self) -> None: def __compress(self) -> None:
""" """
Compress the target path Compress the target path
@ -95,3 +102,4 @@ class Archive():
self.__stdout.ok(f"Compression completed for: {self.item.abspath_target}") self.__stdout.ok(f"Compression completed for: {self.item.abspath_target}")
cmd.cleanup() cmd.cleanup()
self.cleanup()

View file

@ -33,8 +33,8 @@ class Filesystem():
paths = [] paths = []
for item in Config.pathnames: for path in Config.pathnames:
paths.append(item["path_target_from"]) paths.append(path)
return paths return paths
@ -71,6 +71,7 @@ class Filesystem():
Returns: Returns:
str | None: Common pathname with base path or None if no common path (or is base path) str | None: Common pathname with base path or None if no common path (or is base path)
""" """
base_path = os.path.normpath(self.path) base_path = os.path.normpath(self.path)
target_path = os.path.normpath(path) target_path = os.path.normpath(path)

View file

@ -39,12 +39,12 @@ class Config():
""" """
with open(pathname, "r") as f: with open(pathname, "r") as f:
json = json.load(f) config = json.load(f)
for item in json: for item in config:
Config.pathnames.add(item[ConfigKeys.ABSPATH_TARGET.value]) Config.pathnames.add(item[ConfigKeys.ABSPATH_TARGET.value])
Config.for_each(json) return Config.for_each(config)
@staticmethod @staticmethod
def __throw_missing_key(key: ConfigKeys) -> None: def __throw_missing_key(key: ConfigKeys) -> None:
@ -83,7 +83,7 @@ class Config():
self.__item = item self.__item = item
@property @property
def password(self) -> str|False: def password(self) -> str|bool:
""" """
Returns the password for this item, or None if unset Returns the password for this item, or None if unset

View file

@ -103,7 +103,7 @@ class Stdout():
""" """
# Bail out if stdout is disabled # Bail out if stdout is disabled
if Stdout.global_level.value == StdoutLevel.NONE.value: if self.global_level.value == StdoutLevel.NONE.value:
return self.die() return self.die()
print(f"{Format.HEADER.value}> {self.namespace.value}:{Format.ENDC.value}{msg}{Format.ENDC.value}") print(f"{Format.HEADER.value}> {self.namespace.value}:{Format.ENDC.value}{msg}{Format.ENDC.value}")
@ -136,7 +136,7 @@ class Stdout():
""" """
# Bail out if log level is less than verbose # Bail out if log level is less than verbose
if not Stdout.global_level.value >= StdoutLevel.STANDARD.value: if not self.global_level.value >= StdoutLevel.STANDARD.value:
return self.die() return self.die()
return self.print(f" {msg}") return self.print(f" {msg}")
@ -153,7 +153,7 @@ class Stdout():
""" """
# Bail out if log level is less than verbose # Bail out if log level is less than verbose
if not Stdout.global_level.value >= StdoutLevel.VERBOSE.value: if not self.global_level.value >= StdoutLevel.VERBOSE.value:
return self.die() return self.die()
return self.print(f" {msg}") return self.print(f" {msg}")
@ -170,7 +170,7 @@ class Stdout():
""" """
# Bail out if log level is less than verbose # Bail out if log level is less than verbose
if not Stdout.global_level.value >= StdoutLevel.DEBUG.value: if not self.global_level.value >= StdoutLevel.DEBUG.value:
return self.die() return self.die()
return self.print(f" {msg}") return self.print(f" {msg}")
@ -187,7 +187,7 @@ class Stdout():
""" """
# Bail out if log level is less than default # Bail out if log level is less than default
if not Stdout.global_level.value >= StdoutLevel.STANDARD.value: if not self.global_level.value >= StdoutLevel.STANDARD.value:
return self.die() return self.die()
return self.print(f" {Format.WARNING.value}! WARN: {msg}") return self.print(f" {Format.WARNING.value}! WARN: {msg}")

View file

@ -26,6 +26,7 @@ class Aws():
self.__stdout.log(f"Starting upload of archive for: {self.archive.item.abspath_target}") self.__stdout.log(f"Starting upload of archive for: {self.archive.item.abspath_target}")
self.__stdout.debug(f"Archive object: {self.archive}") self.__stdout.debug(f"Archive object: {self.archive}")
self.__stdout.info(f"Uploading to: {self.archive.item.abspath_destination}")
args = [ args = [
"aws", "aws",
@ -53,4 +54,4 @@ class Aws():
cmd.cleanup() cmd.cleanup()
self.archive.cleanup() self.archive.cleanup()
self.__stdout.ok(f"Archive uploaded: {self.archive.item.abspath_target}") self.__stdout.ok(f"Archive uploaded to: {self.archive.item.abspath_destination}")