mirror of https://github.com/borgbase/vorta
Add a dev mode that allows for local storing of config files and logs (#1682)
Allows vorta to be called with the command-line flag `--development` or `-D` that will make it use a directory in the project tree to store all the settings, logs, and cache. This default directory will be called `.dev_config` and placed in the projects root. Also allows for a custom directory path allowing for multiple "configuration" folders at once. This can be used to prevent the vorta instance that a developer is working on from accessing the configuration files that they have set up for their personal backups. * .gitignore : Add `.dev_config`. * src/vorta/utils.py (parse_args): Add `--development` flag. The default will be `DEFAULT_DIR_FLAG`. * src/vorta/utils.py : Add `DEFAULT_DIR_FLAG`. * src/vorta/config.py : Add methods for populating the config directories exposed by this module. * src/vorta/__main__.py (main): Handle `--development` flag and update config directories if its specified. * Access config constants through the `config` module instead of importing them directly with `from .config import`. --------- Co-authored-by: yfprojects <62463991+real-yfprojects@users.noreply.github.com>
This commit is contained in:
parent
5a3a7cf58e
commit
2cb9afd4d5
|
@ -20,6 +20,8 @@ env
|
||||||
venv
|
venv
|
||||||
.env
|
.env
|
||||||
.venv
|
.venv
|
||||||
|
# dirs created by the --development option
|
||||||
|
.dev_config/
|
||||||
# Avoid adding translations of source language
|
# Avoid adding translations of source language
|
||||||
# Files are still used by Transifex
|
# Files are still used by Transifex
|
||||||
src/vorta/i18n/ts/vorta.en.ts
|
src/vorta/i18n/ts/vorta.en.ts
|
||||||
|
|
|
@ -4,13 +4,15 @@ import sys
|
||||||
|
|
||||||
from peewee import SqliteDatabase
|
from peewee import SqliteDatabase
|
||||||
|
|
||||||
|
# Need to import config as a whole module instead of individual variables
|
||||||
|
# because we will be overriding the modules variables
|
||||||
|
from vorta import config
|
||||||
from vorta._version import __version__
|
from vorta._version import __version__
|
||||||
from vorta.config import SETTINGS_DIR
|
|
||||||
from vorta.i18n import trans_late, translate
|
from vorta.i18n import trans_late, translate
|
||||||
from vorta.log import init_logger, logger
|
from vorta.log import init_logger, logger
|
||||||
from vorta.store.connection import init_db
|
from vorta.store.connection import init_db
|
||||||
from vorta.updater import get_updater
|
from vorta.updater import get_updater
|
||||||
from vorta.utils import parse_args
|
from vorta.utils import DEFAULT_DIR_FLAG, parse_args
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
@ -48,6 +50,7 @@ def main():
|
||||||
|
|
||||||
want_version = getattr(args, 'version', False)
|
want_version = getattr(args, 'version', False)
|
||||||
want_background = getattr(args, 'daemonize', False)
|
want_background = getattr(args, 'daemonize', False)
|
||||||
|
want_development = getattr(args, 'development', False)
|
||||||
|
|
||||||
if want_version:
|
if want_version:
|
||||||
print(f"Vorta {__version__}") # noqa: T201
|
print(f"Vorta {__version__}") # noqa: T201
|
||||||
|
@ -57,11 +60,20 @@ def main():
|
||||||
if os.fork():
|
if os.fork():
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
||||||
|
if want_development:
|
||||||
|
# if we're using the default dev dir
|
||||||
|
if want_development is DEFAULT_DIR_FLAG:
|
||||||
|
config.init_dev_mode(config.default_dev_dir())
|
||||||
|
else:
|
||||||
|
# if we're not using the default dev dir and
|
||||||
|
# instead we're using whatever dir is passed as an argument
|
||||||
|
config.init_dev_mode(want_development)
|
||||||
|
|
||||||
init_logger(background=want_background)
|
init_logger(background=want_background)
|
||||||
|
|
||||||
# Init database
|
# Init database
|
||||||
sqlite_db = SqliteDatabase(
|
sqlite_db = SqliteDatabase(
|
||||||
SETTINGS_DIR / 'settings.db',
|
config.SETTINGS_DIR / 'settings.db',
|
||||||
pragmas={
|
pragmas={
|
||||||
'journal_mode': 'wal',
|
'journal_mode': 'wal',
|
||||||
},
|
},
|
||||||
|
|
|
@ -7,11 +7,11 @@ from typing import Any, Dict, List, Tuple
|
||||||
from PyQt6 import QtCore
|
from PyQt6 import QtCore
|
||||||
from PyQt6.QtWidgets import QMessageBox
|
from PyQt6.QtWidgets import QMessageBox
|
||||||
|
|
||||||
|
from vorta import config
|
||||||
from vorta.borg.break_lock import BorgBreakJob
|
from vorta.borg.break_lock import BorgBreakJob
|
||||||
from vorta.borg.create import BorgCreateJob
|
from vorta.borg.create import BorgCreateJob
|
||||||
from vorta.borg.jobs_manager import JobsManager
|
from vorta.borg.jobs_manager import JobsManager
|
||||||
from vorta.borg.version import BorgVersionJob
|
from vorta.borg.version import BorgVersionJob
|
||||||
from vorta.config import LOG_DIR, PROFILE_BOOTSTRAP_FILE, TEMP_DIR
|
|
||||||
from vorta.i18n import init_translations, translate
|
from vorta.i18n import init_translations, translate
|
||||||
from vorta.notifications import VortaNotifications
|
from vorta.notifications import VortaNotifications
|
||||||
from vorta.profile_export import ProfileExport
|
from vorta.profile_export import ProfileExport
|
||||||
|
@ -25,7 +25,7 @@ from vorta.views.main_window import MainWindow
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
APP_ID = TEMP_DIR / "socket"
|
APP_ID = config.TEMP_DIR / "socket"
|
||||||
|
|
||||||
|
|
||||||
class VortaApp(QtSingleApplication):
|
class VortaApp(QtSingleApplication):
|
||||||
|
@ -261,7 +261,11 @@ class VortaApp(QtSingleApplication):
|
||||||
job = BorgBreakJob(params['cmd'], params)
|
job = BorgBreakJob(params['cmd'], params)
|
||||||
self.jobs_manager.add_job(job)
|
self.jobs_manager.add_job(job)
|
||||||
|
|
||||||
def bootstrap_profile(self, bootstrap_file=PROFILE_BOOTSTRAP_FILE):
|
def bootstrap_profile(self, bootstrap_file=None):
|
||||||
|
# Necessary to dynamically load the variable from config during runtime
|
||||||
|
# Check out pull request for #1682 for context
|
||||||
|
bootstrap_file = bootstrap_file or config.PROFILE_BOOTSTRAP_FILE
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Make sure there is at least one profile when first starting Vorta.
|
Make sure there is at least one profile when first starting Vorta.
|
||||||
Will either import a profile placed in ~/.vorta-init.json
|
Will either import a profile placed in ~/.vorta-init.json
|
||||||
|
@ -334,7 +338,7 @@ class VortaApp(QtSingleApplication):
|
||||||
msg.setIcon(QMessageBox.Icon.Warning)
|
msg.setIcon(QMessageBox.Icon.Warning)
|
||||||
text = translate(
|
text = translate(
|
||||||
'VortaApp', 'Borg exited with warning status (rc 1). See the <a href="{0}">logs</a> for details.'
|
'VortaApp', 'Borg exited with warning status (rc 1). See the <a href="{0}">logs</a> for details.'
|
||||||
).format(LOG_DIR.as_uri())
|
).format(config.LOG_DIR.as_uri())
|
||||||
infotext = error_message
|
infotext = error_message
|
||||||
elif returncode > 128:
|
elif returncode > 128:
|
||||||
# 128+N - killed by signal N (e.g. 137 == kill -9)
|
# 128+N - killed by signal N (e.g. 137 == kill -9)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from vorta.config import LOG_DIR
|
from vorta import config
|
||||||
from vorta.i18n import translate
|
from vorta.i18n import translate
|
||||||
from vorta.utils import borg_compat
|
from vorta.utils import borg_compat
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ class BorgCheckJob(BorgJob):
|
||||||
self.app.backup_progress_event.emit(
|
self.app.backup_progress_event.emit(
|
||||||
f"[{self.params['profile_name']}] "
|
f"[{self.params['profile_name']}] "
|
||||||
+ translate('RepoCheckJob', 'Repo check failed. See the <a href="{0}">logs</a> for details.').format(
|
+ translate('RepoCheckJob', 'Repo check failed. See the <a href="{0}">logs</a> for details.').format(
|
||||||
LOG_DIR.as_uri()
|
config.LOG_DIR.as_uri()
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
self.app.check_failed_event.emit(result)
|
self.app.check_failed_event.emit(result)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
from typing import Any, Dict
|
from typing import Any, Dict
|
||||||
|
|
||||||
from vorta.config import LOG_DIR
|
from vorta import config
|
||||||
from vorta.i18n import trans_late, translate
|
from vorta.i18n import trans_late, translate
|
||||||
from vorta.utils import borg_compat
|
from vorta.utils import borg_compat
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ class BorgCompactJob(BorgJob):
|
||||||
f"[{self.params['profile_name']}] "
|
f"[{self.params['profile_name']}] "
|
||||||
+ translate(
|
+ translate(
|
||||||
'BorgCompactJob', 'Errors during compaction. See the <a href="{0}">logs</a> for details.'
|
'BorgCompactJob', 'Errors during compaction. See the <a href="{0}">logs</a> for details.'
|
||||||
).format(LOG_DIR.as_uri())
|
).format(config.LOG_DIR.as_uri())
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Compaction completed.')}")
|
self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Compaction completed.')}")
|
||||||
|
|
|
@ -3,7 +3,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
from datetime import datetime as dt
|
from datetime import datetime as dt
|
||||||
|
|
||||||
from vorta.config import LOG_DIR
|
from vorta import config
|
||||||
from vorta.i18n import trans_late, translate
|
from vorta.i18n import trans_late, translate
|
||||||
from vorta.store.models import (
|
from vorta.store.models import (
|
||||||
ArchiveModel,
|
ArchiveModel,
|
||||||
|
@ -46,7 +46,7 @@ class BorgCreateJob(BorgJob):
|
||||||
+ translate(
|
+ translate(
|
||||||
'BorgCreateJob',
|
'BorgCreateJob',
|
||||||
'Backup finished with warnings. See the <a href="{0}">logs</a> for details.',
|
'Backup finished with warnings. See the <a href="{0}">logs</a> for details.',
|
||||||
).format(LOG_DIR.as_uri())
|
).format(config.LOG_DIR.as_uri())
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
self.app.backup_log_event.emit('', {})
|
self.app.backup_log_event.emit('', {})
|
||||||
|
|
|
@ -5,14 +5,57 @@ import platformdirs
|
||||||
APP_NAME = 'Vorta'
|
APP_NAME = 'Vorta'
|
||||||
APP_AUTHOR = 'BorgBase'
|
APP_AUTHOR = 'BorgBase'
|
||||||
APP_ID_DARWIN = 'com.borgbase.client.macos'
|
APP_ID_DARWIN = 'com.borgbase.client.macos'
|
||||||
dirs = platformdirs.PlatformDirs(APP_NAME, APP_AUTHOR)
|
SETTINGS_DIR = None
|
||||||
SETTINGS_DIR = dirs.user_data_path
|
LOG_DIR = None
|
||||||
LOG_DIR = dirs.user_log_path
|
CACHE_DIR = None
|
||||||
CACHE_DIR = dirs.user_cache_path
|
TEMP_DIR = None
|
||||||
TEMP_DIR = CACHE_DIR / "tmp"
|
PROFILE_BOOTSTRAP_FILE = None
|
||||||
PROFILE_BOOTSTRAP_FILE = Path.home() / '.vorta-init.json'
|
|
||||||
|
|
||||||
|
|
||||||
# ensure directories exist
|
def default_dev_dir() -> Path:
|
||||||
for dir in (SETTINGS_DIR, LOG_DIR, CACHE_DIR, TEMP_DIR):
|
"""Returns a default dir for config files in the project's main folder"""
|
||||||
dir.mkdir(parents=True, exist_ok=True)
|
return Path(__file__).parent.parent.parent / '.dev_config'
|
||||||
|
|
||||||
|
|
||||||
|
def init_from_platformdirs():
|
||||||
|
"""Initializes config dirs for system-wide use"""
|
||||||
|
dirs = platformdirs.PlatformDirs(APP_NAME, APP_AUTHOR)
|
||||||
|
init(dirs.user_data_path, dirs.user_log_path, dirs.user_cache_path, dirs.user_cache_path / 'tmp', Path.home())
|
||||||
|
|
||||||
|
|
||||||
|
def init_dev_mode(dir: Path):
|
||||||
|
"""Initializes config dirs for local use inside provided dir"""
|
||||||
|
dir_full_path = Path(dir).resolve()
|
||||||
|
init(
|
||||||
|
dir_full_path / 'settings',
|
||||||
|
dir_full_path / 'logs',
|
||||||
|
dir_full_path / 'cache',
|
||||||
|
dir_full_path / 'tmp',
|
||||||
|
dir_full_path,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def init(settings: Path, logs: Path, cache: Path, tmp: Path, bootstrap: Path):
|
||||||
|
"""Initializes config directories with provided paths"""
|
||||||
|
global SETTINGS_DIR
|
||||||
|
global LOG_DIR
|
||||||
|
global CACHE_DIR
|
||||||
|
global TEMP_DIR
|
||||||
|
global PROFILE_BOOTSTRAP_FILE
|
||||||
|
SETTINGS_DIR = settings
|
||||||
|
LOG_DIR = logs
|
||||||
|
CACHE_DIR = cache
|
||||||
|
TEMP_DIR = tmp
|
||||||
|
PROFILE_BOOTSTRAP_FILE = bootstrap / '.vorta-init.json'
|
||||||
|
ensure_dirs()
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_dirs():
|
||||||
|
"""Creates config dirs and parent dirs if they don't exist"""
|
||||||
|
# ensure directories exist
|
||||||
|
for dir in (SETTINGS_DIR, LOG_DIR, CACHE_DIR, TEMP_DIR):
|
||||||
|
dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
# Make sure that the config values are valid
|
||||||
|
init_from_platformdirs()
|
||||||
|
|
|
@ -9,7 +9,7 @@ Set up logging to user log dir. Uses the platform's default location:
|
||||||
import logging
|
import logging
|
||||||
from logging.handlers import TimedRotatingFileHandler
|
from logging.handlers import TimedRotatingFileHandler
|
||||||
|
|
||||||
from .config import LOG_DIR
|
from vorta import config
|
||||||
|
|
||||||
logger = logging.getLogger()
|
logger = logging.getLogger()
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ def init_logger(background=False):
|
||||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
# create handlers
|
# create handlers
|
||||||
fh = TimedRotatingFileHandler(LOG_DIR / 'vorta.log', when='d', interval=1, backupCount=5)
|
fh = TimedRotatingFileHandler(config.LOG_DIR / 'vorta.log', when='d', interval=1, backupCount=5)
|
||||||
fh.setLevel(logging.DEBUG)
|
fh.setLevel(logging.DEBUG)
|
||||||
fh.setFormatter(formatter)
|
fh.setFormatter(formatter)
|
||||||
logger.addHandler(fh)
|
logger.addHandler(fh)
|
||||||
|
|
|
@ -22,6 +22,10 @@ from vorta.i18n import trans_late
|
||||||
from vorta.log import logger
|
from vorta.log import logger
|
||||||
from vorta.network_status.abc import NetworkStatusMonitor
|
from vorta.network_status.abc import NetworkStatusMonitor
|
||||||
|
|
||||||
|
# Used to store whether a user wanted to override the
|
||||||
|
# default directory for the --development flag
|
||||||
|
DEFAULT_DIR_FLAG = object()
|
||||||
|
|
||||||
borg_compat = BorgCompatibility()
|
borg_compat = BorgCompatibility()
|
||||||
_network_status_monitor = None
|
_network_status_monitor = None
|
||||||
|
|
||||||
|
@ -353,7 +357,21 @@ def parse_args():
|
||||||
help='Create a backup in the background using the given profile. '
|
help='Create a backup in the background using the given profile. '
|
||||||
'Vorta must already be running for this to work.',
|
'Vorta must already be running for this to work.',
|
||||||
)
|
)
|
||||||
|
# the "development" attribute will be None if the flag is not called
|
||||||
|
# if the flag is called without an extra argument, the "development" attribute
|
||||||
|
# will be set to the value of DEFAULT_DIR_FLAG.
|
||||||
|
# if the flag is called with an extra argument, the "development" attribute
|
||||||
|
# will be set to that argument
|
||||||
|
parser.add_argument(
|
||||||
|
'--development',
|
||||||
|
'-D',
|
||||||
|
nargs='?',
|
||||||
|
const=DEFAULT_DIR_FLAG,
|
||||||
|
metavar="CONFIG_DIRECTORY",
|
||||||
|
help='Start vorta in a local development environment. '
|
||||||
|
'All log, config, cache, and temp files will be stored within the project tree. '
|
||||||
|
'You can follow this flag with an optional path and it will store the files in the provided location.',
|
||||||
|
)
|
||||||
return parser.parse_known_args()[0]
|
return parser.parse_known_args()[0]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@ from PyQt6.QtWidgets import (
|
||||||
QSpacerItem,
|
QSpacerItem,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from vorta import config
|
||||||
from vorta._version import __version__
|
from vorta._version import __version__
|
||||||
from vorta.config import LOG_DIR
|
|
||||||
from vorta.i18n import translate
|
from vorta.i18n import translate
|
||||||
from vorta.store.models import BackupProfileMixin, SettingsModel
|
from vorta.store.models import BackupProfileMixin, SettingsModel
|
||||||
from vorta.store.settings import get_misc_settings
|
from vorta.store.settings import get_misc_settings
|
||||||
|
@ -34,7 +34,8 @@ class MiscTab(MiscTabBase, MiscTabUI, BackupProfileMixin):
|
||||||
self.setupUi(parent)
|
self.setupUi(parent)
|
||||||
self.versionLabel.setText(__version__)
|
self.versionLabel.setText(__version__)
|
||||||
self.logLink.setText(
|
self.logLink.setText(
|
||||||
f'<a href="file://{LOG_DIR}"><span style="text-decoration:' 'underline; color:#0984e3;">Log</span></a>'
|
f'<a href="file://{config.LOG_DIR}"><span style="text-decoration:'
|
||||||
|
'underline; color:#0984e3;">Log</span></a>'
|
||||||
)
|
)
|
||||||
|
|
||||||
self.checkboxLayout = QFormLayout(self.frameSettings)
|
self.checkboxLayout = QFormLayout(self.frameSettings)
|
||||||
|
|
Loading…
Reference in New Issue