Track successful sync runs

This commit is contained in:
liuji
2026-10-10 17:39:58 +08:00
parent baae470d78
commit 50af6ffb52
3 changed files with 84 additions and 21 deletions
+37 -13
View File
@@ -10,9 +10,11 @@ import subprocess
import sys
import tempfile
import zipfile
from datetime import datetime, timezone
from io import BytesIO
from pathlib import Path
from urllib.parse import urlencode
from zoneinfo import ZoneInfo
import xml.etree.ElementTree as ET
import requests
@@ -21,6 +23,7 @@ import requests
ROOT = Path(__file__).resolve().parent
WORKBOOK_ID = os.getenv("WORKBOOK_ID", "1DPbdVsX3SG_kgDXs2j34mDAgplRSfcAWKKG9yqalFmw")
PUBLISH_PATH = Path("data/merged.html")
SYNC_LOG_PATH = Path("data/sync-log.json")
GVIZ_PREFIX = "google.visualization.Query.setResponse("
@@ -171,18 +174,28 @@ def run_git(args: list[str], env: dict[str, str]) -> subprocess.CompletedProcess
)
def publish(content: bytes) -> None:
target = ROOT / PUBLISH_PATH
if target.exists() and target.read_bytes() == content:
print("data/merged.html is unchanged")
def publish(content: bytes, sync_log: dict[str, object]) -> None:
log_content = (json.dumps(sync_log, ensure_ascii=False, indent=2) + "\n").encode("utf-8")
targets = {
PUBLISH_PATH: content,
SYNC_LOG_PATH: log_content,
}
changed = False
for relative_path, target_content in targets.items():
target = ROOT / relative_path
if not target.exists() or target.read_bytes() != target_content:
changed = True
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(target_content)
if not changed:
print("data/merged.html and data/sync-log.json are unchanged")
return
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(content)
env = os.environ.copy()
env["GIT_TERMINAL_PROMPT"] = "0"
askpass_path: str | None = None
credential = env.get("GIT_CREDENTIAL_SECRET", "")
paths = [str(PUBLISH_PATH), str(SYNC_LOG_PATH)]
if credential:
with tempfile.NamedTemporaryFile("w", prefix="ttshop-askpass-", delete=False) as file:
file.write(
@@ -197,22 +210,23 @@ def publish(content: bytes) -> None:
env["GIT_ASKPASS"] = askpass_path
try:
run_git(["add", "--", str(PUBLISH_PATH)], env)
run_git(["add", "--", *paths], env)
staged = subprocess.run(
["git", "diff", "--cached", "--quiet", "--", str(PUBLISH_PATH)],
["git", "diff", "--cached", "--quiet", "--", *paths],
cwd=ROOT, env=env, check=False,
)
if staged.returncode not in (0, 1):
raise RuntimeError("could not inspect staged output")
if staged.returncode == 0:
print("data/merged.html is unchanged")
print("data/merged.html and data/sync-log.json are unchanged")
return
run_git([
"-c", "user.name=TTSHOP Sync", "-c", "user.email=ttshop-sync@localhost",
"commit", "--no-gpg-sign", "-m", "Update merged Google Sheet",
"commit", "--only", "--no-gpg-sign", "-m", "Update merged Google Sheet and sync log",
"--", *paths,
], env)
run_git(["push", "origin", "HEAD:main"], env)
print("published data/merged.html")
print("published data/merged.html and data/sync-log.json")
finally:
if askpass_path:
Path(askpass_path).unlink(missing_ok=True)
@@ -222,13 +236,23 @@ def sync() -> None:
export_url = f"https://docs.google.com/spreadsheets/d/{WORKBOOK_ID}/export?format=xlsx"
response = requests.get(export_url, timeout=30)
response.raise_for_status()
worksheet_names = sheet_names(response.content)
sheets = []
for name in sheet_names(response.content):
for name in worksheet_names:
headers, rows = normalize_sheet(name, read_gviz(name))
if headers:
sheets.append((name, headers, rows))
headers, rows = merge_sheets(sheets)
publish(render_html(headers, rows))
content = render_html(headers, rows)
success_at = datetime.now(timezone.utc)
sync_log = {
"last_success_at": success_at.isoformat().replace("+00:00", "Z"),
"last_success_at_shanghai": success_at.astimezone(ZoneInfo("Asia/Shanghai")).isoformat(),
"worksheet_count": len(worksheet_names),
"record_count": len(rows),
"artifact_bytes": len(content),
}
publish(content, sync_log)
def notify_failure(error: Exception) -> None: