Compare commits
No commits in common. "3cb2c862175b753c012b2439cb3ee2c5e072570d" and "fd29bf93eedc960e0199f2e332d85ddc85cc4af8" have entirely different histories.
3cb2c86217
...
fd29bf93ee
@ -12,11 +12,6 @@ Loop prevention, which was structural back when only Spanish could be a source:
|
|||||||
never itself treated as a source;
|
never itself treated as a source;
|
||||||
* the bot's own commits carry [skip-translate] and are skipped outright.
|
* the bot's own commits carry [skip-translate] and are skipped outright.
|
||||||
|
|
||||||
Deletion is handled too, and has to be: removing a post in the CMS deletes one
|
|
||||||
file, the original, and the siblings this script wrote would otherwise stay on
|
|
||||||
the site as posts in a language with no original. Any sibling whose source has
|
|
||||||
disappeared is removed, unless it is frozen.
|
|
||||||
|
|
||||||
`manual_translation: true` means "hands off", on both sides:
|
`manual_translation: true` means "hands off", on both sides:
|
||||||
* on an authored source — do not generate siblings for this post at all;
|
* on an authored source — do not generate siblings for this post at all;
|
||||||
* on a generated sibling — never overwrite it again.
|
* on a generated sibling — never overwrite it again.
|
||||||
@ -185,41 +180,6 @@ def missing_siblings() -> list[Path]:
|
|||||||
return incomplete
|
return incomplete
|
||||||
|
|
||||||
|
|
||||||
def orphaned_siblings() -> list[Path]:
|
|
||||||
"""Generated siblings whose source no longer exists.
|
|
||||||
|
|
||||||
Deleting a post in the CMS removes one file — the Spanish original. The
|
|
||||||
German and Portuguese siblings were written by this script, not by the
|
|
||||||
author, so nothing else deletes them and they stay on the site as posts in
|
|
||||||
a language that has no original. A rename leaves the same debris, since it
|
|
||||||
is a delete plus an add.
|
|
||||||
|
|
||||||
This scans the whole content tree rather than the push diff, so it also
|
|
||||||
clears siblings orphaned by earlier runs that predate this check.
|
|
||||||
"""
|
|
||||||
orphans = []
|
|
||||||
for path in sorted(CONTENT_DIR.rglob("*.md")):
|
|
||||||
parsed = split_lang(path)
|
|
||||||
if not parsed:
|
|
||||||
continue
|
|
||||||
basename, lang = parsed
|
|
||||||
fm = read_frontmatter(path)
|
|
||||||
source_lang = fm.get("translated_from")
|
|
||||||
if not source_lang:
|
|
||||||
continue # authored by a human; only its author deletes it
|
|
||||||
if path.with_name(f"{basename}.{source_lang}.md").exists():
|
|
||||||
continue
|
|
||||||
rel = path.relative_to(REPO_ROOT)
|
|
||||||
if is_frozen(fm):
|
|
||||||
# Someone edited this translation by hand. Deleting it would throw
|
|
||||||
# that work away on the strength of an inference, so say so instead.
|
|
||||||
print(f" {rel}: source is gone but manual_translation=true — left in place; "
|
|
||||||
f"delete it by hand if the post is meant to disappear")
|
|
||||||
continue
|
|
||||||
orphans.append(path)
|
|
||||||
return orphans
|
|
||||||
|
|
||||||
|
|
||||||
def translate_file(source: Path, basename: str, src_lang: str, provider) -> list[Path]:
|
def translate_file(source: Path, basename: str, src_lang: str, provider) -> list[Path]:
|
||||||
fm, body = split_frontmatter(source.read_text(encoding="utf-8"))
|
fm, body = split_frontmatter(source.read_text(encoding="utf-8"))
|
||||||
written: list[Path] = []
|
written: list[Path] = []
|
||||||
@ -258,7 +218,7 @@ def translate_file(source: Path, basename: str, src_lang: str, provider) -> list
|
|||||||
return written
|
return written
|
||||||
|
|
||||||
|
|
||||||
def commit_and_push(files: list[Path], summary: str) -> None:
|
def commit_and_push(files: list[Path]) -> None:
|
||||||
branch = os.environ.get("CI_COMMIT_BRANCH", "main")
|
branch = os.environ.get("CI_COMMIT_BRANCH", "main")
|
||||||
run("git", "config", "user.name", BOT_NAME)
|
run("git", "config", "user.name", BOT_NAME)
|
||||||
run("git", "config", "user.email", BOT_EMAIL)
|
run("git", "config", "user.email", BOT_EMAIL)
|
||||||
@ -267,13 +227,11 @@ def commit_and_push(files: list[Path], summary: str) -> None:
|
|||||||
if token: # clone credentials are read-only; pushing needs the bot token
|
if token: # clone credentials are read-only; pushing needs the bot token
|
||||||
run("git", "remote", "set-url", "origin",
|
run("git", "remote", "set-url", "origin",
|
||||||
f"https://{BOT_NAME}:{token}@git.vienalatina.com/{repo}.git")
|
f"https://{BOT_NAME}:{token}@git.vienalatina.com/{repo}.git")
|
||||||
# --all so a path that no longer exists stages as a deletion rather than
|
run("git", "add", *[str(f) for f in files])
|
||||||
# failing; reaped orphans arrive here alongside freshly written siblings.
|
|
||||||
run("git", "add", "--all", "--", *[str(f) for f in files])
|
|
||||||
if not run("git", "status", "--porcelain"):
|
if not run("git", "status", "--porcelain"):
|
||||||
print("Translations identical to committed siblings — nothing to push.")
|
print("Translations identical to committed siblings — nothing to push.")
|
||||||
return
|
return
|
||||||
run("git", "commit", "-m", f"translate: {summary} {SKIP_MARKER}")
|
run("git", "commit", "-m", f"translate: update generated siblings {SKIP_MARKER}")
|
||||||
run("git", "push", "origin", f"HEAD:{branch}")
|
run("git", "push", "origin", f"HEAD:{branch}")
|
||||||
print(f"Pushed sibling commit to {branch}.")
|
print(f"Pushed sibling commit to {branch}.")
|
||||||
|
|
||||||
@ -292,39 +250,23 @@ def main() -> None:
|
|||||||
|
|
||||||
paths = missing_siblings() if args.backfill else changed_markdown()
|
paths = missing_siblings() if args.backfill else changed_markdown()
|
||||||
sources = authored_sources(paths)
|
sources = authored_sources(paths)
|
||||||
|
if not sources:
|
||||||
written: list[Path] = []
|
|
||||||
if sources:
|
|
||||||
provider = build_provider()
|
|
||||||
for source, basename, lang in sources:
|
|
||||||
print(f"Translating {source.relative_to(REPO_ROOT)} (from {lang}):")
|
|
||||||
written.extend(translate_file(source, basename, lang, provider))
|
|
||||||
else:
|
|
||||||
print("No authored content changed — nothing to translate.")
|
print("No authored content changed — nothing to translate.")
|
||||||
|
return
|
||||||
|
|
||||||
# Runs whether or not anything was translated: a push that only deletes
|
provider = build_provider()
|
||||||
# posts reaches this point with no sources at all, and that is exactly the
|
written: list[Path] = []
|
||||||
# case where siblings are left stranded.
|
for source, basename, lang in sources:
|
||||||
removed = []
|
print(f"Translating {source.relative_to(REPO_ROOT)} (from {lang}):")
|
||||||
for orphan in orphaned_siblings():
|
written.extend(translate_file(source, basename, lang, provider))
|
||||||
orphan.unlink()
|
|
||||||
removed.append(orphan)
|
|
||||||
print(f" {orphan.relative_to(REPO_ROOT)}: source deleted — removed")
|
|
||||||
|
|
||||||
touched = written + removed
|
if not written:
|
||||||
if not touched:
|
print("Every sibling is frozen — nothing written.")
|
||||||
print("Nothing to commit.")
|
|
||||||
return
|
return
|
||||||
if args.no_push:
|
if args.no_push:
|
||||||
print(f"--no-push: wrote {len(written)} and removed {len(removed)} files, "
|
print(f"--no-push: wrote {len(written)} files, leaving them uncommitted.")
|
||||||
f"leaving them uncommitted.")
|
|
||||||
return
|
return
|
||||||
parts = []
|
commit_and_push(written)
|
||||||
if written:
|
|
||||||
parts.append(f"update {len(written)} generated siblings")
|
|
||||||
if removed:
|
|
||||||
parts.append(f"remove {len(removed)} orphaned by a deleted source")
|
|
||||||
commit_and_push(touched, " and ".join(parts))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user