diff --git a/CHANGELOG.md b/CHANGELOG.md index cc92d899e..55b2409c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ - Added support for PrusaSlicer (via @visika) - Added support for Logseq (via @visika) - Added support for SwitchHosts (via @zxjlm) +- Added support for Microsoft OneDrive as a new storage engine (via @burck1) ## Mackup 0.8.36 diff --git a/README.md b/README.md index 82a5ad83e..16e11987f 100644 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ in it stay put, so that any other computer also running Mackup is unaffected. - [Google Drive](https://drive.google.com/) - [Copy](https://www.copy.com/) - [iCloud](http://www.apple.com/icloud/) +- [Microsoft OneDrive](https://www.microsoft.com/en-us/microsoft-365/onedrive/online-cloud-storage/) - Anything able to sync a folder (e.g. [Git](http://git-scm.com/)) See the [README](doc/README.md) file in the doc directory for more info. diff --git a/doc/.mackup.cfg b/doc/.mackup.cfg index 26dcf1a16..350b22b96 100644 --- a/doc/.mackup.cfg +++ b/doc/.mackup.cfg @@ -2,22 +2,26 @@ # Sample Mackup configuration file # -# You can specify the storage type Mackup will use to store your configuration -# files. -# For now you have 3 options: "dropbox", "google_drive" and "file_system". +# You can specify the storage type Mackup will use to store your configuration files. +# Options include: dropbox, google_drive, icloud, microsoft_onedrive, copy, file_system # If none is specified, Mackup will try to use the default: "dropbox". # With the "dropbox" storage engine, Mackup will automatically figure out your # Dropbox folder. [storage] engine = dropbox -# If you choose the "google_drive" storage engine instead, Mackup will figure -# out where your Google Drive is and store your configuration files in it. +# Similarily, if you choose the "google_drive", "icloud", "microsoft_onedrive" or "copy" +# storage engine, Mackup will figure out your path and store your configuration files in it. + # [storage] # engine = google_drive -# If you choose the "copy" storage engine, Mackup will figure -# out where your Copy folder is and store your configuration files in it. +# [storage] +# engine = icloud + +# [storage] +# engine = microsoft_onedrive + # [storage] # engine = copy diff --git a/doc/README.md b/doc/README.md index 405e08b6f..b0a3c48df 100644 --- a/doc/README.md +++ b/doc/README.md @@ -12,9 +12,14 @@ vi ~/.mackup.cfg ## Storage You can specify the storage type Mackup will use to store your configuration -files. +files. Options include: -For now, you have 4 options: `dropbox`, `google_drive`, `icloud`, `copy` and `file_system`. +- `dropbox` +- `google_drive` +- `icloud` +- `microsoft_onedrive` +- `copy` +- `file_system` If none is specified, Mackup will try to use the default: `dropbox`. With the `dropbox` storage engine, Mackup will automatically figure out your @@ -44,6 +49,13 @@ engine = google_drive engine = icloud ``` +### Microsoft OneDrive + +```ini +[storage] +engine = microsoft_onedrive +``` + ### Copy If you choose the `copy` storage engine, Mackup will figure out diff --git a/mackup/application.py b/mackup/application.py index 7ed410e24..7e7522dc2 100644 --- a/mackup/application.py +++ b/mackup/application.py @@ -72,7 +72,6 @@ def backup(self): and (os.path.isfile(mackup_filepath) or os.path.isdir(mackup_filepath)) and os.path.samefile(home_filepath, mackup_filepath) ): - if self.verbose: print( "Backing up\n {}\n to\n {} ...".format( @@ -87,7 +86,6 @@ def backup(self): # Check if we already have a backup if os.path.exists(mackup_filepath): - # Name it right if os.path.isfile(mackup_filepath): file_type = "file" diff --git a/mackup/config.py b/mackup/config.py index 5fbbc9140..d696b5a6b 100644 --- a/mackup/config.py +++ b/mackup/config.py @@ -12,6 +12,7 @@ ENGINE_COPY, ENGINE_ICLOUD, ENGINE_FS, + ENGINE_MSONEDRIVE, ) from .utils import ( @@ -20,6 +21,7 @@ get_copy_folder_location, get_google_drive_folder_location, get_icloud_folder_location, + get_microsoft_onedrive_folder_location, ) try: @@ -68,7 +70,7 @@ def engine(self): """ The engine used by the storage. - ENGINE_DROPBOX, ENGINE_GDRIVE, ENGINE_COPY, ENGINE_ICLOUD or ENGINE_FS. + ENGINE_DROPBOX, ENGINE_GDRIVE, ENGINE_COPY, ENGINE_ICLOUD, ENGINE_MSONEDRIVE, or ENGINE_FS. Returns: str @@ -194,6 +196,7 @@ def _parse_engine(self): ENGINE_COPY, ENGINE_ICLOUD, ENGINE_FS, + ENGINE_MSONEDRIVE, ]: raise ConfigError("Unknown storage engine: {}".format(engine)) @@ -214,6 +217,8 @@ def _parse_path(self): path = get_copy_folder_location() elif self.engine == ENGINE_ICLOUD: path = get_icloud_folder_location() + elif self.engine == ENGINE_MSONEDRIVE: + path = get_microsoft_onedrive_folder_location() elif self.engine == ENGINE_FS: if self._parser.has_option("storage", "path"): cfg_path = self._parser.get("storage", "path") diff --git a/mackup/constants.py b/mackup/constants.py index 1685f19e7..e0b3ff19a 100644 --- a/mackup/constants.py +++ b/mackup/constants.py @@ -27,6 +27,7 @@ ENGINE_FS = "file_system" ENGINE_GDRIVE = "google_drive" ENGINE_ICLOUD = "icloud" +ENGINE_MSONEDRIVE = "microsoft_onedrive" DOCUMENTATION_URL = "https://github.com/lra/mackup/blob/master/doc/README.md" diff --git a/mackup/main.py b/mackup/main.py index 9e55fba3f..198ac2563 100644 --- a/mackup/main.py +++ b/mackup/main.py @@ -132,7 +132,6 @@ def printAppHeader(app_name): "Are you sure?" ) ): - # Uninstall the apps except Mackup, which we'll uninstall last, to # keep the settings as long as possible app_names = mckp.get_apps_to_backup() diff --git a/mackup/utils.py b/mackup/utils.py index 7ebc73e30..eec251ea2 100644 --- a/mackup/utils.py +++ b/mackup/utils.py @@ -298,6 +298,39 @@ def get_icloud_folder_location(): return str(icloud_home) +def get_microsoft_onedrive_folder_location(): + """ + Try to locate the Microsoft OneDrive folder. + + Returns: + (str) Full path to the current Microsoft OneDrive folder + """ + # the OneDrive sync path should always be found at ~/Library/CloudStorage/OneDrive-*/ + onedrive_paths = get_cloud_storage_paths("OneDrive-") + if not onedrive_paths: + error( + constants.ERROR_UNABLE_TO_FIND_STORAGE.format( + provider="Microsoft OneDrive install" + ) + ) + # if there are multiple OneDrive paths, choose the first + return sorted(onedrive_paths, key=lambda p: p.name)[0].path + + +def get_cloud_storage_paths(prefix=None): + # The Cloud Storage path is found at ~/Library/CloudStorage/. This is the standard path used for all + # storage providers as of macOS 10.15+. ref: https://developer.apple.com/documentation/fileprovider. + cloud_storage_root_path = os.path.join(os.environ["HOME"], "Library/CloudStorage") + if not os.path.exists(cloud_storage_root_path): + return [] + cloud_storage_paths = (p for p in os.scandir(cloud_storage_root_path) if p.is_dir()) + if prefix: + cloud_storage_paths = ( + p for p in cloud_storage_paths if p.name.lower().startswith(prefix.lower()) + ) + return cloud_storage_paths + + def is_process_running(process_name): """ Check if a process with the given name is running. diff --git a/setup.py b/setup.py index 6d61e73f5..bf9ca637a 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ author_email="analogue@glop.org", url="https://github.com/lra/mackup", description="Keep your application settings in sync (macOS/Linux)", - keywords="configuration config dotfiles sync backup dropbox gdrive box", + keywords="configuration config dotfiles sync backup dropbox gdrive icloud onedrive", license="GPLv3", packages=["mackup"], install_requires=["docopt", "six"], diff --git a/tests/config_tests.py b/tests/config_tests.py index fa07c8c2c..ff750ca41 100644 --- a/tests/config_tests.py +++ b/tests/config_tests.py @@ -7,6 +7,7 @@ ENGINE_COPY, ENGINE_ICLOUD, ENGINE_FS, + ENGINE_MSONEDRIVE, ) from mackup.config import Config, ConfigError @@ -168,6 +169,22 @@ def test_config_engine_icloud(self): assert cfg.apps_to_ignore == set(["subversion", "sequel-pro", "sabnzbd"]) assert cfg.apps_to_sync == set(["sublime-text-3", "x11", "sabnzbd"]) + def test_config_engine_microsoft_onedrive(self): + cfg = Config("mackup-engine-microsoft_onedrive.cfg") + + assert isinstance(cfg.engine, str) + assert cfg.engine == ENGINE_MSONEDRIVE + + assert isinstance(cfg.path, str) + assert cfg.path == os.path.join( + os.environ["HOME"], "Library/CloudStorage/OneDrive-Personal" + ) + + assert isinstance(cfg.fullpath, str) + assert cfg.fullpath == os.path.join( + os.environ["HOME"], "Library/CloudStorage/OneDrive-Personal/Mackup" + ) + def test_config_engine_filesystem_no_path(self): with self.assertRaises(ConfigError): Config("mackup-engine-file_system-no_path.cfg") diff --git a/tests/fixtures/Library/CloudStorage/OneDrive-Personal/_blank_.md b/tests/fixtures/Library/CloudStorage/OneDrive-Personal/_blank_.md new file mode 100644 index 000000000..06c937b02 --- /dev/null +++ b/tests/fixtures/Library/CloudStorage/OneDrive-Personal/_blank_.md @@ -0,0 +1 @@ +Blank file for git sync diff --git a/tests/fixtures/mackup-engine-microsoft_onedrive.cfg b/tests/fixtures/mackup-engine-microsoft_onedrive.cfg new file mode 100644 index 000000000..f3f6494e3 --- /dev/null +++ b/tests/fixtures/mackup-engine-microsoft_onedrive.cfg @@ -0,0 +1,2 @@ +[storage] +engine = microsoft_onedrive