Compare commits
11 Commits
376a6d3a25
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| d09fce309f | |||
| 9fd114d1b0 | |||
| 2853d2e125 | |||
| 759d4a6e23 | |||
| 376115a9de | |||
| 9ffe38cbbc | |||
| 4d0fbff039 | |||
| 6681779552 | |||
| 24d062fc5d | |||
| 745844f721 | |||
| 45a8d6163e |
@@ -7,3 +7,4 @@ __pycache__/
|
||||
.active-item.json
|
||||
.workflow-state.json
|
||||
agent-spawns/
|
||||
notes.md
|
||||
|
||||
+19
-1
@@ -81,6 +81,8 @@ PENDING_DIR: Path = Path(
|
||||
str(Path.home() / ".local" / "share" / "coe-mirror" / "pending"),
|
||||
)
|
||||
).expanduser()
|
||||
_MOUNT_ROOT_VAL = _resolve("COE_MIRROR_MOUNT_ROOT", _CFG.get("mount_root"), "")
|
||||
MOUNT_ROOT: Optional[Path] = Path(_MOUNT_ROOT_VAL).expanduser() if _MOUNT_ROOT_VAL else None
|
||||
LOG_DIR = Path.home() / ".local" / "share" / "coe-mirror"
|
||||
LOG_FILE = LOG_DIR / "coe-mirror.log"
|
||||
LOCK_NAME = "coe-mirror.lock"
|
||||
@@ -166,8 +168,19 @@ def release_lock(lock_fd: int) -> None:
|
||||
|
||||
|
||||
def probe_target() -> bool:
|
||||
"""Probe target directory health. Write tiny file, read back, delete. Return True if healthy."""
|
||||
"""Probe target directory health. Write tiny file, read back, delete. Return True if healthy.
|
||||
|
||||
When MOUNT_ROOT is configured, first verify it is a live mountpoint and TARGET_DIR is under it; return False (queue to pending) otherwise.
|
||||
"""
|
||||
try:
|
||||
if MOUNT_ROOT is not None:
|
||||
if not MOUNT_ROOT.exists() or not os.path.ismount(str(MOUNT_ROOT)):
|
||||
logger.warning("mount_root %s is not a live mountpoint; target unavailable; queuing locally", MOUNT_ROOT)
|
||||
return False
|
||||
if not TARGET_DIR.is_relative_to(MOUNT_ROOT):
|
||||
logger.warning("target_dir %s is not under configured mount_root %s; misconfig; queuing locally", TARGET_DIR, MOUNT_ROOT)
|
||||
return False
|
||||
|
||||
probe_file = TARGET_DIR / ".write_probe"
|
||||
TARGET_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
@@ -673,6 +686,11 @@ api_base = "https://git.churchofmalware.org"
|
||||
# Where mirrored zips are stored. One subdirectory per owner is created here.
|
||||
target_dir = "~/coe-mirror"
|
||||
|
||||
# Optional: require this path to be a live mountpoint before writing. If set and
|
||||
# not currently mounted, the run queues to pending instead of creating a phantom
|
||||
# local directory at the mountpoint. Leave blank to disable.
|
||||
# mount_root = "~/pCloudDrive"
|
||||
|
||||
# Optional: local staging when target_dir is unavailable.
|
||||
# pending_dir = "~/.local/share/coe-mirror/pending"
|
||||
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# coe-mirror notes
|
||||
|
||||
- **Created**: 2026-06-11. Purpose: autonomous mirroring of Nightmare_Eclipse repos from Church of Malware to local pCloud storage.
|
||||
- **Source API**: Forgejo public REST at `git.churchofmalware.org`, no auth needed.
|
||||
- **Owner being mirrored**: `Nightmare_Eclipse` (8 active repos as of creation).
|
||||
- **Destination**: `~/pCloudDrive/Hacking/Nightmare_Eclipse/`.
|
||||
- **Schedule**: 00:00 + 12:00 daily, catch-up on boot via Persistent timer + OnBootSec.
|
||||
- **Design rationale**:
|
||||
- FUSE-safe placement uses `shutil.copy2 + size verify + unlink`, not `os.rename`, to handle pCloud FUSE semantics.
|
||||
- pCloud daemon auto-start was rejected — no user unit exists; DISPLAY=:0 spawning is unreliable. Tool relies on Persistent timer + pending-flush for recovery.
|
||||
- `commit.sha` is NOT in the repo-list API response — two-call approach (list, then per-repo branch) is retained.
|
||||
- **DevTrack**: Not created at creation time (oraculo offline, Tailscale last seen 2d ago, carbon roaming off home LAN). Add a `coe-mirror` project + item retroactively when oraculo returns.
|
||||
- **Matrix notification**: Stub-only at creation time, Matrix server down. Real implementation deferred until home Matrix server is back.
|
||||
|
||||
## 2026-06-14 — Matrix notification swap
|
||||
|
||||
- Replaced `notify()` stub with real Synapse send. Targets General room (`!MBpPtYlQYMcngtpvJt:m.mealeyfamily.com`).
|
||||
- Auth: password login via `MATRIX_USER` + `MATRIX_PASSWORD` from Infisical `matrix/` at refresh time. Token cache at `~/.local/share/coe-mirror/matrix-token.json` (chmod 0600), refreshed when <30 min left or on 401.
|
||||
- Non-fatal failures — Matrix outage does not break the mirror.
|
||||
- Env overrides: `COE_MIRROR_MATRIX_ROOM`, `COE_MIRROR_MATRIX_HOMESERVER`.
|
||||
- Pre-deploy step: invite `@intel-bot:m.mealeyfamily.com` to General once Synapse is back. Without joined membership, sends will 403.
|
||||
- DevTrack: #1172.
|
||||
@@ -37,6 +37,7 @@ def mock_paths(tmp_path, monkeypatch):
|
||||
pending_dir.mkdir()
|
||||
log_dir.mkdir()
|
||||
|
||||
monkeypatch.setattr(coe_mirror, "MOUNT_ROOT", None)
|
||||
monkeypatch.setattr(coe_mirror, "TARGET_DIR", target_dir)
|
||||
monkeypatch.setattr(coe_mirror, "PENDING_DIR", pending_dir)
|
||||
monkeypatch.setattr(coe_mirror, "LOG_DIR", log_dir)
|
||||
@@ -595,6 +596,51 @@ class TestPlaceIntoPloudSameFileIsNoop:
|
||||
assert test_file.stat().st_size > 0
|
||||
|
||||
|
||||
class TestMountRootGuard:
|
||||
"""Test probe_target() mount-root verification (phantom-dir regression guard)."""
|
||||
|
||||
def test_no_mount_root_configured_unchanged_behavior(self, mock_paths, setup_logging, monkeypatch):
|
||||
"""MOUNT_ROOT None + writable TARGET_DIR -> True (unchanged behavior)."""
|
||||
monkeypatch.setattr(coe_mirror, "MOUNT_ROOT", None)
|
||||
|
||||
assert coe_mirror.probe_target() is True
|
||||
|
||||
def test_mount_root_live_target_under_it_returns_true(self, mock_paths, setup_logging, monkeypatch, tmp_path):
|
||||
"""MOUNT_ROOT set, live mount, TARGET_DIR a subdir of it -> True."""
|
||||
mount_root = tmp_path / "pCloudDrive"
|
||||
mount_root.mkdir()
|
||||
target_dir = mount_root / "Hacking" / "ChurchOfMalware"
|
||||
monkeypatch.setattr(coe_mirror, "MOUNT_ROOT", mount_root)
|
||||
monkeypatch.setattr(coe_mirror, "TARGET_DIR", target_dir)
|
||||
monkeypatch.setattr(coe_mirror.os.path, "ismount", lambda p: True)
|
||||
|
||||
assert coe_mirror.probe_target() is True
|
||||
|
||||
def test_mount_not_live_does_not_create_phantom_dir(self, mock_paths, setup_logging, monkeypatch, tmp_path):
|
||||
"""MOUNT_ROOT set but not mounted -> False, and TARGET_DIR is never created."""
|
||||
mount_root = tmp_path / "pCloudDrive"
|
||||
mount_root.mkdir()
|
||||
target_dir = mount_root / "Hacking" / "ChurchOfMalware"
|
||||
monkeypatch.setattr(coe_mirror, "MOUNT_ROOT", mount_root)
|
||||
monkeypatch.setattr(coe_mirror, "TARGET_DIR", target_dir)
|
||||
monkeypatch.setattr(coe_mirror.os.path, "ismount", lambda p: False)
|
||||
|
||||
assert coe_mirror.probe_target() is False
|
||||
assert not target_dir.exists()
|
||||
|
||||
def test_target_dir_not_under_mount_root_returns_false(self, mock_paths, setup_logging, monkeypatch, tmp_path):
|
||||
"""MOUNT_ROOT set, live mount, but TARGET_DIR is unrelated -> False, no dir created."""
|
||||
mount_root = tmp_path / "pCloudDrive"
|
||||
mount_root.mkdir()
|
||||
target_dir = tmp_path / "unrelated" / "target"
|
||||
monkeypatch.setattr(coe_mirror, "MOUNT_ROOT", mount_root)
|
||||
monkeypatch.setattr(coe_mirror, "TARGET_DIR", target_dir)
|
||||
monkeypatch.setattr(coe_mirror.os.path, "ismount", lambda p: True)
|
||||
|
||||
assert coe_mirror.probe_target() is False
|
||||
assert not target_dir.exists()
|
||||
|
||||
|
||||
# Tests for Matrix notification implementation.
|
||||
# Note: Tests requiring monkeypatched subprocess.run must use raising=False because
|
||||
# subprocess.run is not a class attribute on the coe_mirror module by default
|
||||
|
||||
Reference in New Issue
Block a user