Arregla l'esborrat a Windows: lleva el bit de només-lectura del .git
This commit is contained in:
+29
-2
@@ -10,7 +10,9 @@ import datetime as _dt
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from collections.abc import Callable
|
||||
@@ -49,6 +51,31 @@ def _noop(_: str) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def _force_rmtree(path: Path, log: LogFn = _noop) -> None:
|
||||
"""Esborra un arbre de fitxers, fins i tot amb fitxers de només-lectura.
|
||||
|
||||
A Windows els objectes de ``.git`` es creen com a només-lectura i fan que
|
||||
``shutil.rmtree`` falli amb ``PermissionError``; cal llevar el bit d'escriptura
|
||||
i reintentar. A diferència de ``ignore_errors=True``, aquí els errors que no
|
||||
puguem resoldre es registren al log en lloc d'empassar-se en silenci.
|
||||
"""
|
||||
if not path.exists():
|
||||
return
|
||||
|
||||
def handle(func: Callable, p: str, _exc: object) -> None:
|
||||
try:
|
||||
os.chmod(p, stat.S_IWRITE)
|
||||
func(p) # reintenta l'operació (unlink/rmdir) que havia fallat
|
||||
except OSError as exc:
|
||||
log(f"No s'ha pogut esborrar {p}: {exc}")
|
||||
|
||||
# Python 3.12 va renombrar el paràmetre `onerror` a `onexc`; suportem tots dos.
|
||||
if sys.version_info >= (3, 12):
|
||||
shutil.rmtree(path, onexc=handle)
|
||||
else:
|
||||
shutil.rmtree(path, onerror=handle)
|
||||
|
||||
|
||||
def _auth_args(token: str) -> list[str]:
|
||||
"""Args -c para autenticar git ante Gitea con un token, sin tocar .git/config."""
|
||||
if not token:
|
||||
@@ -166,7 +193,7 @@ def delete_local(root: Path, game: Game, log: LogFn = _noop) -> None:
|
||||
log(f"{game.name}: no hi ha res a esborrar")
|
||||
return
|
||||
log(f"Esborrant la descàrrega local de {game.name}…")
|
||||
shutil.rmtree(target, ignore_errors=True)
|
||||
_force_rmtree(target, log)
|
||||
|
||||
|
||||
def download(
|
||||
@@ -201,7 +228,7 @@ def download(
|
||||
else:
|
||||
log(f"Clonant {game.name}…")
|
||||
if repo.exists(): # carpeta a medias sin .git: limpiarla
|
||||
shutil.rmtree(repo, ignore_errors=True)
|
||||
_force_rmtree(repo, log)
|
||||
_run_git(
|
||||
["clone", game.clone_url, str(repo)],
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user