Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for glob pattern in application config files #1807

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
- Added support for notion-enhancer (via @fharper)
- Added support for GitFox (via @L3K0V)
- Updated support for Bartender through SetApp (via @dbhagen)
- Added support for glob pattern in application config files (via @jneuendorf
and @semkagtn)

## Mackup 0.8.33

Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.PHONY: develop undevelop lint test clean release black

develop:
pipenv run python setup.py develop

Expand Down
3 changes: 1 addition & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ docopt = "*"
six = "*"

[dev-packages]
# Black is still a pre-release. Replaced with `brew install black` for now.
# black = "*"
black = "*"
coverage = "*"
nose = "*"
twine = "*"
284 changes: 175 additions & 109 deletions Pipfile.lock

Large diffs are not rendered by default.

37 changes: 30 additions & 7 deletions mackup/appsdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
The Applications Database provides an easy to use interface to load application
data from the Mackup Database (files).
"""
import codecs
import glob
import os

try:
Expand Down Expand Up @@ -31,28 +33,36 @@ def __init__(self):
# Needed to not lowercase the configuration_files in the ini files
config.optionxform = str

if config.read(config_file):
try:
valid_config = config.read(config_file)
except UnicodeEncodeError:
with codecs.open(config_file, "r", "utf8") as fp:
valid_config = config.readfp(fp)

if valid_config:
# Get the filename without the directory name
filename = os.path.basename(config_file)
# The app name is the cfg filename with the extension
# The app name is the cfg filename without the extension
app_name = filename[: -len(".cfg")]

# Start building a dict for this app
self.apps[app_name] = dict()
app = dict()

# Add the fancy name for the app, for display purpose
app_pretty_name = config.get("application", "name")
self.apps[app_name]["name"] = app_pretty_name
app["name"] = app_pretty_name

# Add the configuration files to sync
self.apps[app_name]["configuration_files"] = set()
app["configuration_files"] = set()
if config.has_section("configuration_files"):
for path in config.options("configuration_files"):
if path.startswith("/"):
raise ValueError(
"Unsupported absolute path: {}".format(path)
)
self.apps[app_name]["configuration_files"].add(path)
app["configuration_files"] |= self.get_resolved_paths(
path, config
)

# Add the XDG configuration files to sync
home = os.path.expanduser("~/")
Expand All @@ -72,7 +82,11 @@ def __init__(self):
)
path = os.path.join(xdg_config_home, path)
path = path.replace(home, "")
(self.apps[app_name]["configuration_files"].add(path))
app["configuration_files"] |= self.get_resolved_paths(
path, config
)

self.apps[app_name] = app

@staticmethod
def get_config_files():
Expand Down Expand Up @@ -168,3 +182,12 @@ def get_pretty_app_names(self):
pretty_app_names.add(self.get_name(app_name))

return pretty_app_names

def get_resolved_paths(self, path, config):
if config.getboolean("options", "enable_glob", fallback=False):
return {
os.path.relpath(resolved_path, start=os.environ["HOME"])
for resolved_path in glob.glob(os.path.join(os.environ["HOME"], path))
}
else:
return set([path])
2 changes: 1 addition & 1 deletion mackup/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def _setup_parser(self, filename=None):
filename = MACKUP_CONFIG_FILE

parser = configparser.SafeConfigParser(allow_no_value=True)
parser.read(os.path.join(os.path.join(os.environ["HOME"], filename)))
parser.read(os.path.join(os.environ["HOME"], filename))

return parser

Expand Down
21 changes: 16 additions & 5 deletions tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,28 @@ Feel free to add more, the more the better!
## How to run the tests

```bash
cd src/mackup
pip install -r requirements.txt
nosetests
pipenv install --dev
make test
```

And you should see

```
.
................................
Name Stmts Miss Branch BrPart Cover
---------------------------------------------------------
mackup/__init__.py 0 0 0 0 100%
mackup/application.py 88 79 62 0 7%
mackup/appsdb.py 78 17 38 12 66%
mackup/config.py 92 3 34 2 96%
mackup/constants.py 15 0 0 0 100%
mackup/mackup.py 37 22 14 0 33%
mackup/main.py 77 61 32 0 17%
mackup/utils.py 148 7 58 13 90%
---------------------------------------------------------
TOTAL 535 189 238 27 58%
----------------------------------------------------------------------
Ran 1 test in 0.016s
Ran 32 tests in 0.178s

OK
```
Expand Down
72 changes: 72 additions & 0 deletions tests/apps_config_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import os
import tempfile
import unittest
import stat

try:
from unittest.mock import patch
except ImportError:
patch = None


from mackup import utils
from mackup.appsdb import ApplicationsDatabase


realpath = os.path.dirname(os.path.realpath(__file__))
APPLICATIONS_DIR = os.path.join(realpath, "..", "mackup", "applications")
FIXTURES_DIR = os.path.join(realpath, "fixtures")


class TestMackup(unittest.TestCase):
config_file_path = os.path.join(APPLICATIONS_DIR, "test-app.cfg")

def setUp(self):
os.environ["HOME"] = FIXTURES_DIR

with open(self.config_file_path, "wb") as config_file:
config_file.write(
"\n".join(
[
"[application]",
"name = Test App",
"",
"[options]",
"enable_glob = true",
"",
"[configuration_files]",
"Library/Application Support/Test App/*/data.txt",
]
).encode("utf-8")
)

def tearDown(self):
os.remove(self.config_file_path)

def test_glob_configuration_paths(self):
if patch:
with patch.object(
ApplicationsDatabase,
"get_config_files",
return_value=[self.config_file_path],
) as method:
app_db = ApplicationsDatabase()
self.assertEqual(
app_db.get_files("test-app"),
{
"Library/Application Support/Test App/2020/data.txt",
"Library/Application Support/Test App/2021/data.txt",
"Library/Application Support/Test App/2022/data.txt",
},
)
else:
app_db = ApplicationsDatabase()
app_db.get_config_files = lambda *args, **kwargs: [self.config_file_path]
self.assertEqual(
app_db.get_files("test-app"),
{
"Library/Application Support/Test App/2020/data.txt",
"Library/Application Support/Test App/2021/data.txt",
"Library/Application Support/Test App/2022/data.txt",
},
)
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2020
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2021
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2022