Skip to content

Commit 2cb9afd

Browse files
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>
1 parent 5a3a7cf commit 2cb9afd

10 files changed

Lines changed: 107 additions & 27 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ env
2020
venv
2121
.env
2222
.venv
23+
# dirs created by the --development option
24+
.dev_config/
2325
# Avoid adding translations of source language
2426
# Files are still used by Transifex
2527
src/vorta/i18n/ts/vorta.en.ts

src/vorta/__main__.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44

55
from peewee import SqliteDatabase
66

7+
# Need to import config as a whole module instead of individual variables
8+
# because we will be overriding the modules variables
9+
from vorta import config
710
from vorta._version import __version__
8-
from vorta.config import SETTINGS_DIR
911
from vorta.i18n import trans_late, translate
1012
from vorta.log import init_logger, logger
1113
from vorta.store.connection import init_db
1214
from vorta.updater import get_updater
13-
from vorta.utils import parse_args
15+
from vorta.utils import DEFAULT_DIR_FLAG, parse_args
1416

1517

1618
def main():
@@ -48,6 +50,7 @@ def exception_handler(type, value, tb):
4850

4951
want_version = getattr(args, 'version', False)
5052
want_background = getattr(args, 'daemonize', False)
53+
want_development = getattr(args, 'development', False)
5154

5255
if want_version:
5356
print(f"Vorta {__version__}") # noqa: T201
@@ -57,11 +60,20 @@ def exception_handler(type, value, tb):
5760
if os.fork():
5861
sys.exit()
5962

63+
if want_development:
64+
# if we're using the default dev dir
65+
if want_development is DEFAULT_DIR_FLAG:
66+
config.init_dev_mode(config.default_dev_dir())
67+
else:
68+
# if we're not using the default dev dir and
69+
# instead we're using whatever dir is passed as an argument
70+
config.init_dev_mode(want_development)
71+
6072
init_logger(background=want_background)
6173

6274
# Init database
6375
sqlite_db = SqliteDatabase(
64-
SETTINGS_DIR / 'settings.db',
76+
config.SETTINGS_DIR / 'settings.db',
6577
pragmas={
6678
'journal_mode': 'wal',
6779
},

src/vorta/application.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@
77
from PyQt6 import QtCore
88
from PyQt6.QtWidgets import QMessageBox
99

10+
from vorta import config
1011
from vorta.borg.break_lock import BorgBreakJob
1112
from vorta.borg.create import BorgCreateJob
1213
from vorta.borg.jobs_manager import JobsManager
1314
from vorta.borg.version import BorgVersionJob
14-
from vorta.config import LOG_DIR, PROFILE_BOOTSTRAP_FILE, TEMP_DIR
1515
from vorta.i18n import init_translations, translate
1616
from vorta.notifications import VortaNotifications
1717
from vorta.profile_export import ProfileExport
@@ -25,7 +25,7 @@
2525

2626
logger = logging.getLogger(__name__)
2727

28-
APP_ID = TEMP_DIR / "socket"
28+
APP_ID = config.TEMP_DIR / "socket"
2929

3030

3131
class VortaApp(QtSingleApplication):
@@ -261,7 +261,11 @@ def break_lock(self, profile):
261261
job = BorgBreakJob(params['cmd'], params)
262262
self.jobs_manager.add_job(job)
263263

264-
def bootstrap_profile(self, bootstrap_file=PROFILE_BOOTSTRAP_FILE):
264+
def bootstrap_profile(self, bootstrap_file=None):
265+
# Necessary to dynamically load the variable from config during runtime
266+
# Check out pull request for #1682 for context
267+
bootstrap_file = bootstrap_file or config.PROFILE_BOOTSTRAP_FILE
268+
265269
"""
266270
Make sure there is at least one profile when first starting Vorta.
267271
Will either import a profile placed in ~/.vorta-init.json
@@ -334,7 +338,7 @@ def check_failed_response(self, result: Dict[str, Any]):
334338
msg.setIcon(QMessageBox.Icon.Warning)
335339
text = translate(
336340
'VortaApp', 'Borg exited with warning status (rc 1). See the <a href="{0}">logs</a> for details.'
337-
).format(LOG_DIR.as_uri())
341+
).format(config.LOG_DIR.as_uri())
338342
infotext = error_message
339343
elif returncode > 128:
340344
# 128+N - killed by signal N (e.g. 137 == kill -9)

src/vorta/borg/check.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Dict
22

3-
from vorta.config import LOG_DIR
3+
from vorta import config
44
from vorta.i18n import translate
55
from vorta.utils import borg_compat
66

@@ -27,7 +27,7 @@ def finished_event(self, result: Dict[str, Any]):
2727
self.app.backup_progress_event.emit(
2828
f"[{self.params['profile_name']}] "
2929
+ translate('RepoCheckJob', 'Repo check failed. See the <a href="{0}">logs</a> for details.').format(
30-
LOG_DIR.as_uri()
30+
config.LOG_DIR.as_uri()
3131
)
3232
)
3333
self.app.check_failed_event.emit(result)

src/vorta/borg/compact.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from typing import Any, Dict
22

3-
from vorta.config import LOG_DIR
3+
from vorta import config
44
from vorta.i18n import trans_late, translate
55
from vorta.utils import borg_compat
66

@@ -30,7 +30,7 @@ def finished_event(self, result: Dict[str, Any]):
3030
f"[{self.params['profile_name']}] "
3131
+ translate(
3232
'BorgCompactJob', 'Errors during compaction. See the <a href="{0}">logs</a> for details.'
33-
).format(LOG_DIR.as_uri())
33+
).format(config.LOG_DIR.as_uri())
3434
)
3535
else:
3636
self.app.backup_progress_event.emit(f"[{self.params['profile_name']}] {self.tr('Compaction completed.')}")

src/vorta/borg/create.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import tempfile
44
from datetime import datetime as dt
55

6-
from vorta.config import LOG_DIR
6+
from vorta import config
77
from vorta.i18n import trans_late, translate
88
from vorta.store.models import (
99
ArchiveModel,
@@ -46,7 +46,7 @@ def process_result(self, result):
4646
+ translate(
4747
'BorgCreateJob',
4848
'Backup finished with warnings. See the <a href="{0}">logs</a> for details.',
49-
).format(LOG_DIR.as_uri())
49+
).format(config.LOG_DIR.as_uri())
5050
)
5151
else:
5252
self.app.backup_log_event.emit('', {})

src/vorta/config.py

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,57 @@
55
APP_NAME = 'Vorta'
66
APP_AUTHOR = 'BorgBase'
77
APP_ID_DARWIN = 'com.borgbase.client.macos'
8-
dirs = platformdirs.PlatformDirs(APP_NAME, APP_AUTHOR)
9-
SETTINGS_DIR = dirs.user_data_path
10-
LOG_DIR = dirs.user_log_path
11-
CACHE_DIR = dirs.user_cache_path
12-
TEMP_DIR = CACHE_DIR / "tmp"
13-
PROFILE_BOOTSTRAP_FILE = Path.home() / '.vorta-init.json'
8+
SETTINGS_DIR = None
9+
LOG_DIR = None
10+
CACHE_DIR = None
11+
TEMP_DIR = None
12+
PROFILE_BOOTSTRAP_FILE = None
1413

1514

16-
# ensure directories exist
17-
for dir in (SETTINGS_DIR, LOG_DIR, CACHE_DIR, TEMP_DIR):
18-
dir.mkdir(parents=True, exist_ok=True)
15+
def default_dev_dir() -> Path:
16+
"""Returns a default dir for config files in the project's main folder"""
17+
return Path(__file__).parent.parent.parent / '.dev_config'
18+
19+
20+
def init_from_platformdirs():
21+
"""Initializes config dirs for system-wide use"""
22+
dirs = platformdirs.PlatformDirs(APP_NAME, APP_AUTHOR)
23+
init(dirs.user_data_path, dirs.user_log_path, dirs.user_cache_path, dirs.user_cache_path / 'tmp', Path.home())
24+
25+
26+
def init_dev_mode(dir: Path):
27+
"""Initializes config dirs for local use inside provided dir"""
28+
dir_full_path = Path(dir).resolve()
29+
init(
30+
dir_full_path / 'settings',
31+
dir_full_path / 'logs',
32+
dir_full_path / 'cache',
33+
dir_full_path / 'tmp',
34+
dir_full_path,
35+
)
36+
37+
38+
def init(settings: Path, logs: Path, cache: Path, tmp: Path, bootstrap: Path):
39+
"""Initializes config directories with provided paths"""
40+
global SETTINGS_DIR
41+
global LOG_DIR
42+
global CACHE_DIR
43+
global TEMP_DIR
44+
global PROFILE_BOOTSTRAP_FILE
45+
SETTINGS_DIR = settings
46+
LOG_DIR = logs
47+
CACHE_DIR = cache
48+
TEMP_DIR = tmp
49+
PROFILE_BOOTSTRAP_FILE = bootstrap / '.vorta-init.json'
50+
ensure_dirs()
51+
52+
53+
def ensure_dirs():
54+
"""Creates config dirs and parent dirs if they don't exist"""
55+
# ensure directories exist
56+
for dir in (SETTINGS_DIR, LOG_DIR, CACHE_DIR, TEMP_DIR):
57+
dir.mkdir(parents=True, exist_ok=True)
58+
59+
60+
# Make sure that the config values are valid
61+
init_from_platformdirs()

src/vorta/log.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import logging
1010
from logging.handlers import TimedRotatingFileHandler
1111

12-
from .config import LOG_DIR
12+
from vorta import config
1313

1414
logger = logging.getLogger()
1515

@@ -23,7 +23,7 @@ def init_logger(background=False):
2323
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
2424

2525
# create handlers
26-
fh = TimedRotatingFileHandler(LOG_DIR / 'vorta.log', when='d', interval=1, backupCount=5)
26+
fh = TimedRotatingFileHandler(config.LOG_DIR / 'vorta.log', when='d', interval=1, backupCount=5)
2727
fh.setLevel(logging.DEBUG)
2828
fh.setFormatter(formatter)
2929
logger.addHandler(fh)

src/vorta/utils.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@
2222
from vorta.log import logger
2323
from vorta.network_status.abc import NetworkStatusMonitor
2424

25+
# Used to store whether a user wanted to override the
26+
# default directory for the --development flag
27+
DEFAULT_DIR_FLAG = object()
28+
2529
borg_compat = BorgCompatibility()
2630
_network_status_monitor = None
2731

@@ -353,7 +357,21 @@ def parse_args():
353357
help='Create a backup in the background using the given profile. '
354358
'Vorta must already be running for this to work.',
355359
)
356-
360+
# the "development" attribute will be None if the flag is not called
361+
# if the flag is called without an extra argument, the "development" attribute
362+
# will be set to the value of DEFAULT_DIR_FLAG.
363+
# if the flag is called with an extra argument, the "development" attribute
364+
# will be set to that argument
365+
parser.add_argument(
366+
'--development',
367+
'-D',
368+
nargs='?',
369+
const=DEFAULT_DIR_FLAG,
370+
metavar="CONFIG_DIRECTORY",
371+
help='Start vorta in a local development environment. '
372+
'All log, config, cache, and temp files will be stored within the project tree. '
373+
'You can follow this flag with an optional path and it will store the files in the provided location.',
374+
)
357375
return parser.parse_known_args()[0]
358376

359377

src/vorta/views/misc_tab.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
QSpacerItem,
1313
)
1414

15+
from vorta import config
1516
from vorta._version import __version__
16-
from vorta.config import LOG_DIR
1717
from vorta.i18n import translate
1818
from vorta.store.models import BackupProfileMixin, SettingsModel
1919
from vorta.store.settings import get_misc_settings
@@ -34,7 +34,8 @@ def __init__(self, parent=None):
3434
self.setupUi(parent)
3535
self.versionLabel.setText(__version__)
3636
self.logLink.setText(
37-
f'<a href="file://{LOG_DIR}"><span style="text-decoration:' 'underline; color:#0984e3;">Log</span></a>'
37+
f'<a href="file://{config.LOG_DIR}"><span style="text-decoration:'
38+
'underline; color:#0984e3;">Log</span></a>'
3839
)
3940

4041
self.checkboxLayout = QFormLayout(self.frameSettings)

0 commit comments

Comments
 (0)