93 lines
2.7 KiB
Python
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())
|