diff --git a/README.md b/README.md index d187f6e..b21b29c 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ Inspired by the historical figure [Juan Hispalense (siglo XII)](https://es.wikip - Required environment variables: - `ONEDRIVE_CLIENT_ID` - `ONEDRIVE_CLIENT_SECRET` + - `ONEDRIVE_TENANT_ID` - `SRC_FOLDER` (OneDrive folder path) - `OUT_FOLDER` (local output path) - Register an app and configure permissions by following Microsoft's official docs: [Register an application](https://learn.microsoft.com/en-us/graph/auth-register-app-v2). diff --git a/avendehut/cli.py b/avendehut/cli.py index 7309ccb..d112c47 100644 --- a/avendehut/cli.py +++ b/avendehut/cli.py @@ -1,13 +1,9 @@ from __future__ import annotations import sys -import webbrowser -from pathlib import Path -from typing import Optional import click from rich.console import Console -from rich.table import Table from .commands.build import build_command from .commands.watch import watch_command diff --git a/avendehut/commands/build.py b/avendehut/commands/build.py index 4ccd46a..439b01d 100644 --- a/avendehut/commands/build.py +++ b/avendehut/commands/build.py @@ -1,12 +1,10 @@ from __future__ import annotations import hashlib -import json import os -from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path -from typing import Iterable, List, Tuple +from typing import Iterable, List import click from rich.console import Console @@ -40,7 +38,7 @@ def compute_file_hash(path: Path) -> str: @click.command(context_settings={"help_option_names": ["-h", "-help", "--help"]}) -@click.option("--src", type=click.Path(exists=True, file_okay=False, path_type=Path), required=True, help="Source folder (local or onedrive:/path)") +@click.option("--src", type=click.Path(exists=True, file_okay=False, path_type=Path), required=True, help="Source folder (local path)") @click.option("--out", type=click.Path(file_okay=False, path_type=Path), required=True, help="Output folder") @click.option("--format", "format_", type=click.Choice(["html"], case_sensitive=False), default="html", show_default=True) @click.option("--force", is_flag=True, help="Reprocess all files, ignoring manifest") diff --git a/avendehut/commands/export.py b/avendehut/commands/export.py index 53b4bad..1f1c977 100644 --- a/avendehut/commands/export.py +++ b/avendehut/commands/export.py @@ -18,7 +18,10 @@ def export_command(out: Path, format_: str, out_dir: Path) -> None: """Export catalog to CSV or JSON.""" items = list(iter_catalog(out_dir)) if format_ == "json": - out.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8") + # Write atomically to avoid partial files + tmp = out.with_suffix(out.suffix + ".tmp") + tmp.write_text(json.dumps(items, ensure_ascii=False, indent=2), encoding="utf-8") + tmp.replace(out) else: fieldnames = [ "id", @@ -34,7 +37,9 @@ def export_command(out: Path, format_: str, out_dir: Path) -> None: "created_at", "modified_at", ] - with out.open("w", newline="", encoding="utf-8") as f: + # Write atomically to avoid partial files + tmp = out.with_suffix(out.suffix + ".tmp") + with tmp.open("w", newline="", encoding="utf-8") as f: writer = csv.DictWriter(f, fieldnames=fieldnames) writer.writeheader() for it in items: @@ -42,4 +47,5 @@ def export_command(out: Path, format_: str, out_dir: Path) -> None: row["authors"] = ", ".join(row.get("authors", []) or []) row["tags"] = ", ".join(row.get("tags", []) or []) writer.writerow(row) + tmp.replace(out) diff --git a/avendehut/commands/watch.py b/avendehut/commands/watch.py index 5bda87c..4181d63 100644 --- a/avendehut/commands/watch.py +++ b/avendehut/commands/watch.py @@ -26,8 +26,6 @@ def watch_command(src: Path, out: Path, interval: float) -> None: if snapshot != last_snapshot: # Trigger a build console.print("Change detected. Rebuilding...") - # Invoke build_command programmatically - build_command.callback # type: ignore[attr-defined] build_command.main( # type: ignore args=["--src", str(src), "--out", str(out)], prog_name="avendehut build", standalone_mode=False )