-
Notifications
You must be signed in to change notification settings - Fork 8
Rclone #70
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
base: develop
Are you sure you want to change the base?
Rclone #70
Conversation
…ks as to why files are processed mutiple times
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds Rclone as a new provider for the SiteSync addon, enabling file synchronization through Rclone remotes. The implementation follows the existing provider pattern and supports both configuration file-based and environment variable-based authentication.
Changes:
- Added RClone provider settings model with multiplatform path support and both config file and environment variable configuration options
- Implemented RCloneHandler client with full file operations (upload, download, delete, list, create folder) via subprocess calls to rclone
- Registered the new provider in the factory with a batch limit of 20
Reviewed changes
Copilot reviewed 4 out of 5 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| server/settings/settings.py | Imports RClone settings model and registers "rclone" as a provider option |
| server/settings/providers/rclone.py | Defines RCloneSubmodel with configuration fields for rclone executable, config path, remote settings, and authentication |
| client/ayon_sitesync/providers/rclone.py | Implements RCloneHandler class with all required provider methods using rclone subprocess calls |
| client/ayon_sitesync/providers/lib.py | Registers RCloneHandler in the provider factory |
| client/ayon_sitesync/providers/resources/rclone.png | Adds Rclone provider icon |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| cmd = [self.rclone_path, "--config", | ||
| self._config_path] |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When a config path is provided, the code constructs a command including "--config" with an empty string (line 208-209). This could cause issues if self._config_path is an empty string instead of being truly absent. The condition at line 221 checks 'if not self._config_path', which would be True for empty strings, but by then the command has already been built incorrectly. Consider restructuring the logic to handle config path construction only when it's non-empty, or ensure empty strings are treated consistently.
server/settings/providers/rclone.py
Outdated
| description="Egg webdav, Use this if you do not have a rclone.conf") | ||
|
|
||
| url: str = Field("", title="Remote Url", | ||
| description="Use this if you do not have a rclone.conf") | ||
|
|
||
| vendor: str = Field("", title="Vendor", | ||
| description="Egg nextcloud, check rclone docs for the right name, Use this if you do not have a rclone.conf") |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The description contains a typo. "Egg" should be "e.g." (the Latin abbreviation for "exempli gratia" meaning "for example").
| description="Egg webdav, Use this if you do not have a rclone.conf") | |
| url: str = Field("", title="Remote Url", | |
| description="Use this if you do not have a rclone.conf") | |
| vendor: str = Field("", title="Vendor", | |
| description="Egg nextcloud, check rclone docs for the right name, Use this if you do not have a rclone.conf") | |
| description="e.g. webdav, Use this if you do not have a rclone.conf") | |
| url: str = Field("", title="Remote Url", | |
| description="Use this if you do not have a rclone.conf") | |
| vendor: str = Field("", title="Vendor", | |
| description="e.g. nextcloud, check rclone docs for the right name, Use this if you do not have a rclone.conf") |
| env[env_url] = self.url | ||
| env_user = f"RCLONE_CONFIG_{self.remote_name.upper()}_USER" | ||
| env[env_user] = self.user | ||
| env["RCLONE_CONFIG"] = "NUL" |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using "NUL" as the RCLONE_CONFIG value is Windows-specific. On Unix-based systems (Linux/macOS), the null device is "/dev/null". This should use os.devnull which provides a cross-platform null device path, or use conditional logic based on the platform to ensure compatibility across Windows, Linux, and macOS environments.
| env["RCLONE_CONFIG"] = "NUL" | |
| env["RCLONE_CONFIG"] = os.devnull |
| return subprocess.check_output(cmd).decode().strip() | ||
|
|
||
| @staticmethod | ||
| def _parse_rclone_json(output: dict): |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type hint for the 'output' parameter is incorrect. The parameter is typed as 'dict' but the function actually handles string and bytes types (as shown by the isinstance checks on lines 303-304). The type hint should be 'str | bytes' or 'Union[str, bytes]' to accurately reflect the actual parameter types.
| def _parse_rclone_json(output: dict): | |
| def _parse_rclone_json(output: str | bytes): |
server/settings/providers/rclone.py
Outdated
| @@ -0,0 +1,66 @@ | |||
| from pydantic import Field | |||
| from ayon_server.settings import BaseSettingsModel, secrets_enum | |||
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The import secrets_enum is not used anywhere in this file. This should be removed to avoid unused imports.
| from ayon_server.settings import BaseSettingsModel, secrets_enum | |
| from ayon_server.settings import BaseSettingsModel |
server/settings/providers/rclone.py
Outdated
| type: str = Field("", title="Remote Type", | ||
| description="Egg webdav, Use this if you do not have a rclone.conf") | ||
|
|
||
| url: str = Field("", title="Remote Url", | ||
| description="Use this if you do not have a rclone.conf") | ||
|
|
||
| vendor: str = Field("", title="Vendor", | ||
| description="Egg nextcloud, check rclone docs for the right name, Use this if you do not have a rclone.conf") | ||
|
|
||
| user: str = Field("", title="User", | ||
| description="Use this if you do not have a rclone.conf") |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fields 'type', 'url', 'vendor', and 'user' are missing the 'scope' parameter. Looking at other fields in this model and similar settings in other providers (e.g., sftp.py lines 37-41), these fields should have scope defined. Consider adding scope=["studio", "project", "site"] to maintain consistency with other provider-specific configuration fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm I would make them consistent... but I am not sure which ones to take as a baseline... just studio project or all three
| self.rclone_path = self.presets.get("rclone_executable_path", {}).get( | ||
| platform.system().lower(), "rclone") | ||
|
|
||
| self._root = self.presets.get("root", "").strip("/") |
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _root instance variable is set at line 19 but never used anywhere in the class. The get_roots_config method uses self.presets.get('root', '/') instead. Consider removing this unused variable or using it in get_roots_config if it was intended to be used.
| self._root = self.presets.get("root", "").strip("/") |
| def _parse_time(self, rclone_time_str): | ||
| """Parse rclone's ISO8601 time string to timestamp.""" | ||
| dt = datetime.fromisoformat(rclone_time_str.replace("Z", "+00:00")) | ||
| return dt.timestamp() | ||
|
|
Copilot
AI
Jan 29, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _parse_time method is defined but never used anywhere in the class. If this method is not needed for future functionality, it should be removed to avoid maintaining unused code. If it's intended for future use, consider adding a comment explaining its purpose.
| def _parse_time(self, rclone_time_str): | |
| """Parse rclone's ISO8601 time string to timestamp.""" | |
| dt = datetime.fromisoformat(rclone_time_str.replace("Z", "+00:00")) | |
| return dt.timestamp() |
…hints, better handle parsed rclone json output, reduce exceptions from non existing folders
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@kalisp I am not sure about the scope settings... especially with the PW not being obscured and then everyone can see it on the site settings so I set them to project and studio only... would convert the PR now away from draft. |
Changelog Description
Added Rclone as a provider. All is done via subprocess. so rclone needs to be present on PATH or the absolute path needs to be set.
Additional review information
need to double check the linter so leaving this as a draft until then
Testing notes:
Tested with Nextcloud as a rclone remote, via config file and just envvars, following the paradigm of some other providers to implement both. Tested on windows only.