2025-08-07 06:28:34 -04:00
|
|
|
#!/usr/bin/env python3
|
2026-03-11 23:15:07 -04:00
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
2025-08-07 06:28:34 -04:00
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
2025-11-06 03:40:46 -05:00
|
|
|
import os
|
2025-08-07 06:28:34 -04:00
|
|
|
import subprocess
|
2025-11-26 22:20:43 -05:00
|
|
|
import sys
|
2025-08-07 06:28:34 -04:00
|
|
|
|
|
|
|
|
IGNORE_EXTENSION = []
|
|
|
|
|
IGNORE_FILE_NAME = ["Makefile"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def execute_shell(cmd):
|
2025-11-26 22:20:43 -05:00
|
|
|
result = subprocess.run(
|
|
|
|
|
cmd,
|
|
|
|
|
shell=True,
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
)
|
|
|
|
|
output = (result.stdout or "") + (result.stderr or "")
|
|
|
|
|
return result.returncode, output.strip()
|
2025-08-07 06:28:34 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_modified_files():
|
|
|
|
|
lst_cmd_git_status = ["git", "status", "--porcelain"]
|
2025-11-26 00:47:25 -05:00
|
|
|
path_repo_bin = ".venv.erplibre/bin/repo"
|
|
|
|
|
if os.path.exists(path_repo_bin):
|
|
|
|
|
lst_cmd_git_status_repo = [
|
|
|
|
|
path_repo_bin,
|
|
|
|
|
"forall",
|
|
|
|
|
"-p",
|
|
|
|
|
"-c",
|
|
|
|
|
"git status -s",
|
|
|
|
|
]
|
|
|
|
|
else:
|
|
|
|
|
lst_cmd_git_status_repo = []
|
2025-08-07 06:28:34 -04:00
|
|
|
try:
|
2025-11-06 03:40:46 -05:00
|
|
|
print(" ".join(lst_cmd_git_status))
|
2025-08-07 06:28:34 -04:00
|
|
|
result = subprocess.run(
|
|
|
|
|
lst_cmd_git_status,
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
check=True,
|
|
|
|
|
)
|
2025-11-06 03:40:46 -05:00
|
|
|
lines_local = result.stdout.strip().split("\n")
|
|
|
|
|
|
|
|
|
|
lst_lines = [(".", lines_local)]
|
|
|
|
|
|
[FIX] format : rendre repo facultatif, et montrer ce qu'il a dit
Un échec de « repo forall » arrêtait tout : la liste des fichiers locaux, déjà
obtenue, était jetée, et « make format » rendait 0 sans avoir formaté une
ligne. Sa sortie d'erreur, capturée puis jetée, emportait la seule explication,
et seule l'exception Python restait à l'écran. Google Repo n'ajoute pourtant
que les dépôts rapatriés : il est joué sans check, ce qu'il a écrit est relayé,
et les fichiers locaux sont formatés quand même. « Rien à formater » se dit au
lieu d'une sortie muette, et un échec du dépôt lui-même rend non nul.
Vérifié : avec un repo bouchonné qui échoue, son message s'affiche et le
fichier local est formaté.
--- EN ---
A failing "repo forall" stopped everything: the local file list, already
gathered, was dropped, and "make format" returned 0 without formatting a line.
Its error output, captured then thrown away, carried the only explanation, and
only the Python exception stayed on screen. Google Repo only adds the checked
out repositories: it runs without check, whatever it wrote is relayed, and
local files are formatted anyway. "Nothing to format" is said instead of a
silent exit, and a failure of the repository itself returns non-zero.
Checked: with a stub repo that fails, its message shows and the local file is
formatted.
Assisted-by: Claude Opus 5
2026-09-24 13:32:41 -04:00
|
|
|
lines_project = []
|
2025-11-26 00:47:25 -05:00
|
|
|
if lst_cmd_git_status_repo:
|
|
|
|
|
print(" ".join(lst_cmd_git_status_repo))
|
2025-11-06 03:40:46 -05:00
|
|
|
|
[FIX] format : rendre repo facultatif, et montrer ce qu'il a dit
Un échec de « repo forall » arrêtait tout : la liste des fichiers locaux, déjà
obtenue, était jetée, et « make format » rendait 0 sans avoir formaté une
ligne. Sa sortie d'erreur, capturée puis jetée, emportait la seule explication,
et seule l'exception Python restait à l'écran. Google Repo n'ajoute pourtant
que les dépôts rapatriés : il est joué sans check, ce qu'il a écrit est relayé,
et les fichiers locaux sont formatés quand même. « Rien à formater » se dit au
lieu d'une sortie muette, et un échec du dépôt lui-même rend non nul.
Vérifié : avec un repo bouchonné qui échoue, son message s'affiche et le
fichier local est formaté.
--- EN ---
A failing "repo forall" stopped everything: the local file list, already
gathered, was dropped, and "make format" returned 0 without formatting a line.
Its error output, captured then thrown away, carried the only explanation, and
only the Python exception stayed on screen. Google Repo only adds the checked
out repositories: it runs without check, whatever it wrote is relayed, and
local files are formatted anyway. "Nothing to format" is said instead of a
silent exit, and a failure of the repository itself returns non-zero.
Checked: with a stub repo that fails, its message shows and the local file is
formatted.
Assisted-by: Claude Opus 5
2026-09-24 13:32:41 -04:00
|
|
|
# Google Repo est FACULTATIF ici : il n'ajoute que les dépôts
|
|
|
|
|
# rapatriés. Son échec laissait pourtant tomber la liste locale
|
|
|
|
|
# déjà obtenue, et « make format » rendait 0 sans avoir formaté
|
|
|
|
|
# une ligne. Il est donc joué sans check, et ce qu'il a écrit est
|
|
|
|
|
# RELAYÉ : capturée puis jetée, sa sortie d'erreur emportait la
|
|
|
|
|
# seule explication — le hashbang de bin/repo est relatif, si bien
|
|
|
|
|
# qu'un lancement hors de la racine échoue sans dire pourquoi.
|
2025-11-26 00:47:25 -05:00
|
|
|
result = subprocess.run(
|
|
|
|
|
lst_cmd_git_status_repo,
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
)
|
[FIX] format : rendre repo facultatif, et montrer ce qu'il a dit
Un échec de « repo forall » arrêtait tout : la liste des fichiers locaux, déjà
obtenue, était jetée, et « make format » rendait 0 sans avoir formaté une
ligne. Sa sortie d'erreur, capturée puis jetée, emportait la seule explication,
et seule l'exception Python restait à l'écran. Google Repo n'ajoute pourtant
que les dépôts rapatriés : il est joué sans check, ce qu'il a écrit est relayé,
et les fichiers locaux sont formatés quand même. « Rien à formater » se dit au
lieu d'une sortie muette, et un échec du dépôt lui-même rend non nul.
Vérifié : avec un repo bouchonné qui échoue, son message s'affiche et le
fichier local est formaté.
--- EN ---
A failing "repo forall" stopped everything: the local file list, already
gathered, was dropped, and "make format" returned 0 without formatting a line.
Its error output, captured then thrown away, carried the only explanation, and
only the Python exception stayed on screen. Google Repo only adds the checked
out repositories: it runs without check, whatever it wrote is relayed, and
local files are formatted anyway. "Nothing to format" is said instead of a
silent exit, and a failure of the repository itself returns non-zero.
Checked: with a stub repo that fails, its message shows and the local file is
formatted.
Assisted-by: Claude Opus 5
2026-09-24 13:32:41 -04:00
|
|
|
if result.returncode:
|
|
|
|
|
print(
|
|
|
|
|
f"repo forall a rendu {result.returncode} : les depots"
|
|
|
|
|
" rapatries sont ignores, les fichiers locaux sont"
|
|
|
|
|
" formates quand meme."
|
|
|
|
|
)
|
|
|
|
|
for flux in (result.stderr, result.stdout):
|
|
|
|
|
if flux and flux.strip():
|
|
|
|
|
print(f" {flux.strip()}")
|
|
|
|
|
else:
|
|
|
|
|
lines_project = result.stdout.strip().split("\n\n")
|
2025-11-18 18:36:50 -05:00
|
|
|
|
|
|
|
|
if os.path.isfile(".odoo-version"):
|
|
|
|
|
with open(".odoo-version") as txt:
|
|
|
|
|
odoo_version = txt.read()
|
|
|
|
|
path_addons = os.path.join(
|
|
|
|
|
f"odoo{odoo_version}", "addons", "addons"
|
|
|
|
|
)
|
|
|
|
|
if os.path.exists(os.path.join(path_addons, ".git")):
|
|
|
|
|
# Detected hidden private repository
|
|
|
|
|
result = subprocess.run(
|
|
|
|
|
["git", "status", "--porcelain"],
|
|
|
|
|
cwd=path_addons,
|
|
|
|
|
capture_output=True,
|
|
|
|
|
text=True,
|
|
|
|
|
check=True,
|
|
|
|
|
)
|
|
|
|
|
output = result.stdout.strip()
|
|
|
|
|
if output:
|
|
|
|
|
lines_addons_project = f"project {path_addons}/\n" + output
|
|
|
|
|
lines_project.append(lines_addons_project)
|
|
|
|
|
|
2025-11-06 03:40:46 -05:00
|
|
|
for str_project_line in lines_project:
|
|
|
|
|
line_repo = str_project_line.split("\n")
|
|
|
|
|
projet_name = line_repo[0]
|
|
|
|
|
lines_local = line_repo[1:]
|
|
|
|
|
directory_project = projet_name[len("project ") :]
|
|
|
|
|
lst_lines.append((directory_project, lines_local))
|
2025-08-07 06:28:34 -04:00
|
|
|
|
|
|
|
|
modified_files = []
|
2025-11-06 03:40:46 -05:00
|
|
|
for directory, lines in lst_lines:
|
|
|
|
|
for line in lines:
|
|
|
|
|
if not line:
|
|
|
|
|
continue
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
has_space = False
|
|
|
|
|
file_path_space = ""
|
|
|
|
|
if '"' in line:
|
|
|
|
|
has_space = True
|
|
|
|
|
file_path_space = line[
|
|
|
|
|
line.index('"') + 1 : line.rindex('"')
|
|
|
|
|
]
|
|
|
|
|
line = line.replace(f'"{file_path_space}"', "replace")
|
|
|
|
|
if "->" in line:
|
|
|
|
|
# Example : M file_01 -> file_02
|
|
|
|
|
status, old_file_path, code, file_path = (
|
|
|
|
|
line.strip().replace(" ", " ").split(" ")
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
# Example : M file_01
|
|
|
|
|
status, file_path = (
|
|
|
|
|
line.strip().replace(" ", " ").split(" ")
|
|
|
|
|
)
|
|
|
|
|
if has_space:
|
|
|
|
|
file_path = file_path_space
|
2025-11-10 04:47:11 -05:00
|
|
|
# Remove ignoring
|
|
|
|
|
if (
|
|
|
|
|
file_path.endswith(".zip")
|
|
|
|
|
or file_path.endswith(".tar.gz")
|
|
|
|
|
or file_path.endswith("__pycache__/")
|
|
|
|
|
):
|
|
|
|
|
continue
|
2025-11-06 03:40:46 -05:00
|
|
|
file_path = os.path.join(directory, file_path)
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"'{line}'")
|
|
|
|
|
raise e
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
status == "M"
|
|
|
|
|
or status == "A"
|
|
|
|
|
or status == "AM"
|
|
|
|
|
or status == "MM"
|
|
|
|
|
or status == "R"
|
|
|
|
|
or status == "RM"
|
|
|
|
|
or status == "??"
|
|
|
|
|
):
|
|
|
|
|
modified_files.append((status, file_path))
|
|
|
|
|
elif status == "D":
|
|
|
|
|
# Ignore to format removed file
|
|
|
|
|
pass
|
2025-08-07 06:28:34 -04:00
|
|
|
else:
|
2025-11-06 03:40:46 -05:00
|
|
|
print(f"Not supported status '{status}'")
|
2025-08-07 06:28:34 -04:00
|
|
|
|
|
|
|
|
return modified_files
|
|
|
|
|
except subprocess.CalledProcessError as e:
|
|
|
|
|
print(f"Error execution with git command: {e}")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
cmd_format = "parallel ::: "
|
|
|
|
|
has_file = False
|
|
|
|
|
files = get_modified_files()
|
|
|
|
|
if files:
|
|
|
|
|
for status, path in files:
|
|
|
|
|
print(f"Statut: '{status}', Fichier: {path}")
|
|
|
|
|
cmd_format += f"\"./script/maintenance/format.sh '{path}'\" "
|
|
|
|
|
has_file = True
|
|
|
|
|
if has_file:
|
|
|
|
|
print(cmd_format)
|
2025-11-26 22:20:43 -05:00
|
|
|
status, output = execute_shell(cmd_format)
|
|
|
|
|
if status != 0:
|
|
|
|
|
print(output)
|
|
|
|
|
sys.exit(status)
|
[FIX] format : rendre repo facultatif, et montrer ce qu'il a dit
Un échec de « repo forall » arrêtait tout : la liste des fichiers locaux, déjà
obtenue, était jetée, et « make format » rendait 0 sans avoir formaté une
ligne. Sa sortie d'erreur, capturée puis jetée, emportait la seule explication,
et seule l'exception Python restait à l'écran. Google Repo n'ajoute pourtant
que les dépôts rapatriés : il est joué sans check, ce qu'il a écrit est relayé,
et les fichiers locaux sont formatés quand même. « Rien à formater » se dit au
lieu d'une sortie muette, et un échec du dépôt lui-même rend non nul.
Vérifié : avec un repo bouchonné qui échoue, son message s'affiche et le
fichier local est formaté.
--- EN ---
A failing "repo forall" stopped everything: the local file list, already
gathered, was dropped, and "make format" returned 0 without formatting a line.
Its error output, captured then thrown away, carried the only explanation, and
only the Python exception stayed on screen. Google Repo only adds the checked
out repositories: it runs without check, whatever it wrote is relayed, and
local files are formatted anyway. "Nothing to format" is said instead of a
silent exit, and a failure of the repository itself returns non-zero.
Checked: with a stub repo that fails, its message shows and the local file is
formatted.
Assisted-by: Claude Opus 5
2026-09-24 13:32:41 -04:00
|
|
|
elif files is None:
|
|
|
|
|
# get_modified_files a échoué sur le dépôt lui-même : rendre 0
|
|
|
|
|
# laisserait croire que tout est formaté.
|
|
|
|
|
sys.exit(1)
|
|
|
|
|
else:
|
|
|
|
|
print("Aucun fichier modifie a formater.")
|