mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d34c1cb2fa |
@@ -212,6 +212,10 @@ def project_sync(
|
||||
remote_path,
|
||||
"--filter-from",
|
||||
str(filter_path),
|
||||
"--header-download",
|
||||
"X-Tigris-Consistent: true",
|
||||
"--header-upload",
|
||||
"X-Tigris-Consistent: true",
|
||||
]
|
||||
|
||||
if verbose:
|
||||
@@ -287,6 +291,10 @@ def project_bisync(
|
||||
str(filter_path),
|
||||
"--workdir",
|
||||
str(state_path),
|
||||
"--header-download",
|
||||
"X-Tigris-Consistent: true",
|
||||
"--header-upload",
|
||||
"X-Tigris-Consistent: true",
|
||||
]
|
||||
|
||||
# Add --create-empty-src-dirs if rclone version supports it (v1.64+)
|
||||
@@ -356,6 +364,10 @@ def project_check(
|
||||
remote_path,
|
||||
"--filter-from",
|
||||
str(filter_path),
|
||||
"--header-download",
|
||||
"X-Tigris-Consistent: true",
|
||||
"--header-upload",
|
||||
"X-Tigris-Consistent: true",
|
||||
]
|
||||
|
||||
if one_way:
|
||||
@@ -393,6 +405,12 @@ def project_ls(
|
||||
if path:
|
||||
remote_path = f"{remote_path}/{path}"
|
||||
|
||||
cmd = ["rclone", "ls", remote_path]
|
||||
cmd = [
|
||||
"rclone",
|
||||
"ls",
|
||||
remote_path,
|
||||
"--header-download",
|
||||
"X-Tigris-Consistent: true",
|
||||
]
|
||||
result = run(cmd, capture_output=True, text=True, check=True)
|
||||
return result.stdout.splitlines()
|
||||
|
||||
@@ -7,6 +7,7 @@ Uses a single "basic-memory-cloud" remote for all operations.
|
||||
import configparser
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
@@ -22,7 +23,32 @@ class RcloneConfigError(Exception):
|
||||
|
||||
|
||||
def get_rclone_config_path() -> Path:
|
||||
"""Get the path to rclone configuration file."""
|
||||
"""Get the path to rclone configuration file.
|
||||
|
||||
Uses runtime detection via `rclone config file` to support different
|
||||
installation methods (Scoop, Chocolatey, manual) across platforms.
|
||||
Falls back to default location if rclone command fails.
|
||||
"""
|
||||
try:
|
||||
# Use rclone to determine its actual config location
|
||||
result = subprocess.run(
|
||||
["rclone", "config", "file"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
# Output format: "Configuration file is stored at:\n/path/to/rclone.conf"
|
||||
lines = result.stdout.strip().split("\n")
|
||||
if lines:
|
||||
config_path = Path(lines[-1].strip())
|
||||
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
return config_path
|
||||
except (subprocess.SubprocessError, FileNotFoundError):
|
||||
# If rclone is not available or command fails, fall back to default
|
||||
pass
|
||||
|
||||
# Fallback to default location
|
||||
config_dir = Path.home() / ".config" / "rclone"
|
||||
config_dir.mkdir(parents=True, exist_ok=True)
|
||||
return config_dir / "rclone.conf"
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import subprocess
|
||||
import time
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
from basic_memory.cli.commands.cloud.bisync_commands import convert_bmignore_to_rclone_filters
|
||||
from basic_memory.cli.commands.cloud.rclone_config import (
|
||||
@@ -57,6 +60,38 @@ def test_convert_bmignore_to_rclone_filters_is_cached_when_up_to_date(config_hom
|
||||
assert second.stat().st_mtime >= first_mtime
|
||||
|
||||
|
||||
def test_get_rclone_config_path_uses_runtime_detection(monkeypatch, tmp_path):
|
||||
"""Test that get_rclone_config_path uses rclone config file for runtime detection."""
|
||||
config_path = tmp_path / "custom" / "rclone.conf"
|
||||
|
||||
def mock_run(cmd, **kwargs):
|
||||
if cmd == ["rclone", "config", "file"]:
|
||||
result = Mock()
|
||||
result.returncode = 0
|
||||
result.stdout = f"Configuration file is stored at:\n{config_path}"
|
||||
return result
|
||||
raise NotImplementedError(f"Unexpected command: {cmd}")
|
||||
|
||||
monkeypatch.setattr("subprocess.run", mock_run)
|
||||
|
||||
detected_path = get_rclone_config_path()
|
||||
assert detected_path == config_path
|
||||
assert detected_path.parent.exists()
|
||||
|
||||
|
||||
def test_get_rclone_config_path_falls_back_on_error(monkeypatch):
|
||||
"""Test that get_rclone_config_path falls back to default if rclone fails."""
|
||||
|
||||
def mock_run(cmd, **kwargs):
|
||||
raise subprocess.SubprocessError("rclone not found")
|
||||
|
||||
monkeypatch.setattr("subprocess.run", mock_run)
|
||||
|
||||
fallback_path = get_rclone_config_path()
|
||||
expected = Path.home() / ".config" / "rclone" / "rclone.conf"
|
||||
assert fallback_path == expected
|
||||
|
||||
|
||||
def test_configure_rclone_remote_writes_config_and_backs_up_existing(config_home):
|
||||
cfg_path = get_rclone_config_path()
|
||||
cfg_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -130,6 +130,9 @@ def test_project_sync_success(tmp_path):
|
||||
assert "--filter-from" in cmd
|
||||
assert str(filter_path) in cmd
|
||||
assert "--dry-run" in cmd
|
||||
assert "--header-download" in cmd
|
||||
assert "X-Tigris-Consistent: true" in cmd
|
||||
assert "--header-upload" in cmd
|
||||
assert kwargs["text"] is True
|
||||
|
||||
|
||||
@@ -214,6 +217,9 @@ def test_project_bisync_success(tmp_path):
|
||||
assert "--compare=modtime" in cmd
|
||||
assert "--workdir" in cmd
|
||||
assert str(state_path) in cmd
|
||||
assert "--header-download" in cmd
|
||||
assert "X-Tigris-Consistent: true" in cmd
|
||||
assert "--header-upload" in cmd
|
||||
|
||||
|
||||
def test_project_bisync_requires_resync_first_time(tmp_path):
|
||||
@@ -369,6 +375,9 @@ def test_project_check_success(tmp_path):
|
||||
assert result is True
|
||||
cmd, kwargs = runner.calls[0]
|
||||
assert cmd[:2] == ["rclone", "check"]
|
||||
assert "--header-download" in cmd
|
||||
assert "X-Tigris-Consistent: true" in cmd
|
||||
assert "--header-upload" in cmd
|
||||
assert kwargs["capture_output"] is True
|
||||
assert kwargs["text"] is True
|
||||
|
||||
@@ -407,6 +416,9 @@ def test_project_ls_success():
|
||||
project = SyncProject(name="research", path="app/data/research")
|
||||
files = project_ls(project, "my-bucket", run=runner, is_installed=lambda: True)
|
||||
assert files == ["file1.md", "file2.md", "subdir/file3.md"]
|
||||
cmd, _ = runner.calls[0]
|
||||
assert "--header-download" in cmd
|
||||
assert "X-Tigris-Consistent: true" in cmd
|
||||
|
||||
|
||||
def test_project_ls_with_subpath():
|
||||
|
||||
Reference in New Issue
Block a user