diff --git a/README.md b/README.md index f88aa18a8..ce1fdf9a9 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,13 @@ services: - PUID= - GUID= env_file: - - .env.icloud #should contain ENV_ICLOUD_PASSWORD= + - .env.icloud #should contain ENV_ICLOUD_PASSWORD=, ENV_CONFIG_FILE_PATH= container_name: icloud restart: unless-stopped volumes: - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro + # To override /app/config.yaml path in container, specify environment variable ENV_CONFIG_FILE_PATH= - ${PWD}/icloud/config.yaml:/app/config.yaml - ${PWD}/icloud/data:/app/icloud - ${PWD}/session_data:/app/session_data diff --git a/src/__init__.py b/src/__init__.py index af2144c0c..a63a6e4b4 100644 --- a/src/__init__.py +++ b/src/__init__.py @@ -15,6 +15,7 @@ DEFAULT_SYNC_INTERVAL_SEC = 1800 # 30 minutes DEFAULT_CONFIG_FILE_NAME = "config.yaml" ENV_ICLOUD_PASSWORD_KEY = "ENV_ICLOUD_PASSWORD" +ENV_CONFIG_FILE_PATH_KEY = "ENV_CONFIG_FILE_PATH" DEFAULT_LOGGER_LEVEL = "info" DEFAULT_LOG_FILE_NAME = "icloud.log" DEFAULT_CONFIG_FILE_PATH = os.path.join( diff --git a/src/sync.py b/src/sync.py index ad5773d18..b2eb99fb8 100644 --- a/src/sync.py +++ b/src/sync.py @@ -7,7 +7,9 @@ from icloudpy import ICloudPyService, exceptions, utils from src import ( + DEFAULT_CONFIG_FILE_PATH, DEFAULT_COOKIE_DIRECTORY, + ENV_CONFIG_FILE_PATH_KEY, ENV_ICLOUD_PASSWORD_KEY, LOGGER, config_parser, @@ -52,7 +54,11 @@ def sync(): photos_sync_interval = 0 sleep_for = 10 while True: - config = read_config() + config = read_config( + config_path=os.environ.get( + ENV_CONFIG_FILE_PATH_KEY, DEFAULT_CONFIG_FILE_PATH + ) + ) alive(config=config) username = config_parser.get_username(config=config) if username: diff --git a/tests/__init__.py b/tests/__init__.py index 4652d32b8..8bc3cdda5 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -10,6 +10,7 @@ DATA_DIR = os.path.join(os.path.dirname(__file__), "data") CONFIG_PATH = os.path.join(DATA_DIR, "test_config.yaml") +ENV_CONFIG_PATH = os.path.join(DATA_DIR, "test_config_env.yaml") TEMP_DIR = os.path.join(os.path.dirname(__file__), "temp") DRIVE_DIR = os.path.join(TEMP_DIR, "icloud", "drive") PHOTOS_DIR = os.path.join(TEMP_DIR, "icloud", "photos") diff --git a/tests/data/test_config_env.yaml b/tests/data/test_config_env.yaml new file mode 100644 index 000000000..0142ed223 --- /dev/null +++ b/tests/data/test_config_env.yaml @@ -0,0 +1,68 @@ +app: + logger: + # level - debug, info (default), warning or error + level: debug + # log filename icloud.log (default) + filename: config_loaded_using_env_config_path.log + credentials: + # iCloud drive username + username: user@test.com + # Retry failed login (with/without 2FA) every 10 minutes + retry_login_interval: 600 + # Drive destination + root: "./icloud" + smtp: + # If you want to recieve email notifications about expired/missing 2FA credentials then uncomment + # email: sender@test.com + # Uncomment this if your SMTP username is different than your sender address (for services like AWS SES) + # username: "" + # default to is same as email above + # to: receiver@test.com + # password: + # host: smtp.test.com + # port: 587 + # If your email provider doesn't handle TLS + # no_tls: true + # valid values are - global (default - uses .com) or china (uses .com.cn) + region: global + +drive: + destination: "./drive" + remove_obsolete: true + sync_interval: -1 + ignore: + - "*.psd" + - .git/ + filters: + # File filters to be included in syncing iCloud drive content + folders: + - dir1/dir2/dir3 + - Keynote + - icloudpy + - Obsidian + file_extensions: + # File extensions to be included + - pdf + - png + - jpg + - jpeg + - md + - band + - xmcf +photos: + destination: photos + remove_obsolete: false + sync_interval: -1 + all_albums: false # Optional, default false. If true preserve album structure. If same photo is in multpile albums creates duplicates on filesystem + # folder_format: "%Y/%m" # optional, if set put photos in subfolders according to format. Format cheatsheet - https://strftime.org + filters: + # if all_albums is false - albums list is used as filter-in, if all_albums is true - albums list is used as filter-out + # if albums list is empty and all_albums is false download all photos to "all" folder. if empty and all_albums is true download all folders + albums: + - "album 2" + - album-1 + file_sizes: + # Valid values are [original, medium or thumb] + - original + - medium + - thumb diff --git a/tests/test_config_parser.py b/tests/test_config_parser.py index c2b1c9966..b0e5bf83d 100644 --- a/tests/test_config_parser.py +++ b/tests/test_config_parser.py @@ -12,6 +12,7 @@ DEFAULT_RETRY_LOGIN_INTERVAL_SEC, DEFAULT_ROOT_DESTINATION, DEFAULT_SYNC_INTERVAL_SEC, + ENV_CONFIG_FILE_PATH_KEY, config_parser, read_config, ) @@ -20,10 +21,26 @@ class TestConfigParser(unittest.TestCase): """Tests for config parser.""" + def tearDown(self): + """Clean up.""" + if ENV_CONFIG_FILE_PATH_KEY in os.environ: + os.environ.pop(ENV_CONFIG_FILE_PATH_KEY) + def test_read_config_default_config_path(self): """Test for Default config path.""" self.assertIsNotNone(read_config(config_path=tests.CONFIG_PATH)) + def test_read_config_env_config_path(self): + """Test for ENV_CONFIG_FILE_PATH.""" + os.environ[ENV_CONFIG_FILE_PATH_KEY] = tests.ENV_CONFIG_PATH + config = read_config( + config_path=os.environ.get(ENV_CONFIG_FILE_PATH_KEY, tests.CONFIG_PATH) + ) + self.assertEqual( + config["app"]["logger"]["filename"], + "config_loaded_using_env_config_path.log", + ) + def test_read_config_overridden_config_path(self): """Test for Overridden config path.""" self.assertIsNotNone(read_config(config_path=tests.CONFIG_PATH))