trainlog/tools/post_sync_ai_drive.py

93 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""Publish the post-sync TRAINLOG_AI_EXPORT_V1 artifact to Google Drive."""
from __future__ import annotations
import argparse
import subprocess
import sys
from pathlib import Path
DRIVE_TARGET = "TrainLog Gdrive:Trainlog/AI/trainlog_ai_export_v1.json"
def _detail(result: subprocess.CompletedProcess[str]) -> str:
"""Return one bounded, useful external-command diagnostic."""
text = (result.stderr or result.stdout or "").strip()
if not text:
return f"exit={result.returncode}"
return text.splitlines()[-1][:1000]
def publish(database: Path, output: Path) -> int:
"""Export first, then upload only the successfully replaced artifact.
CONTRACT: the exporter remains the sole owner of TRAINLOG_AI_EXPORT_V1
generation. rclone receives only file and remote arguments; Trainlog never
reads, stores, or forwards rclone/OAuth configuration. INVARIANT: an export
failure cannot upload an older file left at the destination path.
"""
project_root = Path(__file__).resolve().parent.parent
resolved_output = output if output.is_absolute() else project_root / output
exporter = project_root / "tools" / "export_ai_history.py"
try:
export = subprocess.run(
[
sys.executable,
str(exporter),
str(resolved_output),
"--database",
str(database),
],
capture_output=True,
text=True,
check=False,
)
except OSError as error:
print(f"AI_EXPORT=FAIL {error}")
print("GDRIVE_UPLOAD=SKIPPED")
return 1
if export.returncode != 0 or "TRAINLOG_AI_EXPORT_V1=PASS" not in export.stdout:
print(f"AI_EXPORT=FAIL {_detail(export)}")
print("GDRIVE_UPLOAD=SKIPPED")
return 1
print("AI_EXPORT=PASS")
try:
upload = subprocess.run(
["rclone", "copyto", str(resolved_output), DRIVE_TARGET],
capture_output=True,
text=True,
check=False,
)
except OSError as error:
print(f"GDRIVE_UPLOAD=FAIL {error}")
return 1
if upload.returncode != 0:
print(f"GDRIVE_UPLOAD=FAIL {_detail(upload)}")
return 1
print("GDRIVE_UPLOAD=PASS")
return 0
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument(
"output",
type=Path,
nargs="?",
default=Path("trainlog_ai_export_v1.json"),
)
parser.add_argument("--database", type=Path, required=True)
args = parser.parse_args()
return publish(args.database, args.output)
if __name__ == "__main__":
raise SystemExit(main())