2026-02-13 01:27:38 -05:00
|
|
|
#!/usr/bin/env python3
|
2026-03-11 23:15:07 -04:00
|
|
|
# © 2021-2026 TechnoLibre (http://www.technolibre.ca)
|
2026-02-13 01:27:38 -05:00
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
|
|
|
|
|
|
|
|
|
import datetime
|
|
|
|
|
import logging
|
|
|
|
|
import os
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
import re
|
2026-02-13 01:27:38 -05:00
|
|
|
import shutil
|
|
|
|
|
import subprocess
|
|
|
|
|
import sys
|
|
|
|
|
import time
|
|
|
|
|
|
2026-02-14 05:53:31 -05:00
|
|
|
try:
|
|
|
|
|
import humanize
|
2026-02-19 12:27:35 -05:00
|
|
|
except ModuleNotFoundError as e:
|
2026-02-14 05:53:31 -05:00
|
|
|
humanize = None
|
2026-02-13 01:27:38 -05:00
|
|
|
|
2026-03-10 03:06:07 -04:00
|
|
|
VENV_ERPLIBRE = ".venv.erplibre"
|
2026-02-13 01:27:38 -05:00
|
|
|
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
# Une commande construite ailleurs peut porter un secret en clair : todo.py et
|
|
|
|
|
# kdbx_manager.py y mettent « --default_password_auth '<mot de passe KeePass>' »,
|
|
|
|
|
# db_restore.py « --master_password=… ». Cette commande est affichée avant et
|
|
|
|
|
# après l'exécution, et journalisée en erreur : le secret finissait donc dans le
|
|
|
|
|
# terminal, dans les journaux et dans toute sortie CI qui les capture.
|
|
|
|
|
#
|
|
|
|
|
# On caviarde la VALEUR, jamais le nom de l'option : la commande reste lisible et
|
|
|
|
|
# reproductible, il ne manque que ce qui ne doit pas être lu.
|
|
|
|
|
_SECRET_OPTION = re.compile(
|
|
|
|
|
r"(?P<opt>--?[\w-]*"
|
|
|
|
|
r"(?:password|passwd|pwd|secret|token|api[-_]?key)[\w-]*"
|
|
|
|
|
r"(?:\s+|=))"
|
|
|
|
|
r"(?P<val>'[^']*'|\"[^\"]*\"|\S+)",
|
|
|
|
|
re.IGNORECASE,
|
|
|
|
|
)
|
|
|
|
|
_SECRET_ENV = re.compile(
|
|
|
|
|
r"(?P<var>\b\w*(?:PASSWORD|PASSWD|SECRET|TOKEN)\w*=)"
|
|
|
|
|
r"(?P<val>'[^']*'|\"[^\"]*\"|\S+)"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def redact_secrets(text):
|
|
|
|
|
"""Remplace la valeur des options et variables porteuses de secret.
|
|
|
|
|
|
|
|
|
|
Appliqué à CHAQUE affichage d'une commande. Filtrer au point d'affichage
|
|
|
|
|
plutôt qu'à la construction est ce qui rend la garantie tenable : il n'y a
|
|
|
|
|
qu'une poignée de sorties ici, alors que les commandes se construisent
|
|
|
|
|
partout dans le dépôt.
|
|
|
|
|
"""
|
|
|
|
|
if not text:
|
|
|
|
|
return text
|
|
|
|
|
text = _SECRET_OPTION.sub(lambda m: m.group("opt") + "'***'", text)
|
|
|
|
|
return _SECRET_ENV.sub(lambda m: m.group("var") + "'***'", text)
|
|
|
|
|
|
|
|
|
|
|
2026-02-13 01:27:38 -05:00
|
|
|
new_path = os.path.normpath(
|
|
|
|
|
os.path.join(os.path.dirname(__file__), "..", "..")
|
|
|
|
|
)
|
|
|
|
|
sys.path.append(new_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logging.basicConfig(
|
|
|
|
|
format=(
|
|
|
|
|
"%(asctime)s,%(msecs)d %(levelname)-8s [%(filename)s:%(lineno)d]"
|
|
|
|
|
" %(message)s"
|
|
|
|
|
),
|
|
|
|
|
datefmt="%Y-%m-%d:%H:%M:%S",
|
|
|
|
|
level=logging.INFO,
|
|
|
|
|
)
|
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Execute:
|
2026-03-10 04:07:38 -04:00
|
|
|
def __init__(self) -> None:
|
|
|
|
|
self.cmd_source_erplibre: str = ""
|
|
|
|
|
self.cmd_source_default: str = ""
|
2026-02-13 01:27:38 -05:00
|
|
|
exec_path_gnome_terminal = shutil.which("gnome-terminal")
|
|
|
|
|
if exec_path_gnome_terminal:
|
|
|
|
|
self.cmd_source_erplibre = (
|
|
|
|
|
f"gnome-terminal -- bash -c 'source"
|
2026-03-10 03:06:07 -04:00
|
|
|
f" ./{VENV_ERPLIBRE}/bin/activate;%s'"
|
2026-02-13 01:27:38 -05:00
|
|
|
)
|
|
|
|
|
self.cmd_source_default = "gnome-terminal -- bash -c '" f"%s'"
|
|
|
|
|
else:
|
|
|
|
|
exec_path_tell = shutil.which("osascript")
|
|
|
|
|
if exec_path_tell:
|
|
|
|
|
self.cmd_source_erplibre = (
|
|
|
|
|
"osascript -e 'tell application \"Terminal\"'"
|
|
|
|
|
)
|
|
|
|
|
self.cmd_source_erplibre += " -e 'tell application \"System Events\" to keystroke \"t\" using {command down}' -e 'delay 0.1' -e 'do script \""
|
2026-03-10 03:06:07 -04:00
|
|
|
self.cmd_source_erplibre += f"cd {os.getcwd()}; source ./{VENV_ERPLIBRE}/bin/activate; %s\" in front window'"
|
2026-02-13 01:27:38 -05:00
|
|
|
self.cmd_source_erplibre += " -e 'end tell'"
|
|
|
|
|
else:
|
|
|
|
|
self.cmd_source_erplibre = (
|
2026-03-10 03:06:07 -04:00
|
|
|
f"source ./{VENV_ERPLIBRE}/bin/activate;%s"
|
2026-02-13 01:27:38 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
def exec_command_live(
|
|
|
|
|
self,
|
2026-03-10 04:07:38 -04:00
|
|
|
command: str,
|
|
|
|
|
source_erplibre: bool = True,
|
|
|
|
|
quiet: bool = False,
|
|
|
|
|
single_source_erplibre: bool = False,
|
|
|
|
|
new_window: bool = False,
|
|
|
|
|
single_source_odoo: bool = False,
|
|
|
|
|
source_odoo: str = "",
|
|
|
|
|
new_env: dict | None = None,
|
|
|
|
|
return_status_and_command: bool = False,
|
|
|
|
|
return_status_and_output: bool = False,
|
|
|
|
|
return_status_and_output_and_command: bool = False,
|
|
|
|
|
) -> (
|
|
|
|
|
int
|
|
|
|
|
| tuple[int, str]
|
|
|
|
|
| tuple[int, list[str]]
|
|
|
|
|
| tuple[int, str, list[str]]
|
2026-02-13 01:27:38 -05:00
|
|
|
):
|
|
|
|
|
"""
|
2026-03-10 03:45:11 -04:00
|
|
|
Execute a command and display its output live.
|
2026-02-13 01:27:38 -05:00
|
|
|
|
|
|
|
|
Args:
|
2026-03-10 03:45:11 -04:00
|
|
|
command (str): The command to execute.
|
2026-02-13 01:27:38 -05:00
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
my_env = os.environ.copy()
|
|
|
|
|
if new_env:
|
|
|
|
|
my_env.update(new_env)
|
|
|
|
|
|
|
|
|
|
process_start_time = time.time()
|
2026-03-10 03:06:07 -04:00
|
|
|
exit_code = None
|
2026-02-13 01:27:38 -05:00
|
|
|
if source_erplibre:
|
2026-03-10 03:06:07 -04:00
|
|
|
# command = f"source ./{VENV_ERPLIBRE}/bin/activate && " + command
|
2026-02-13 01:27:38 -05:00
|
|
|
# cmd = (
|
|
|
|
|
# f"gnome-terminal --tab -- bash -c 'source"
|
2026-03-10 03:06:07 -04:00
|
|
|
# f" ./{VENV_ERPLIBRE}/bin/activate;{command}'"
|
2026-02-13 01:27:38 -05:00
|
|
|
# )
|
2026-03-10 03:06:07 -04:00
|
|
|
command = self.cmd_source_erplibre % command
|
|
|
|
|
# os.system(f"./script/terminal/open_terminal.sh {command}")
|
2026-02-13 01:27:38 -05:00
|
|
|
elif single_source_erplibre:
|
2026-03-10 04:07:38 -04:00
|
|
|
command = f"source ./{VENV_ERPLIBRE}/bin/activate && %s" % command
|
2026-02-13 01:27:38 -05:00
|
|
|
elif single_source_odoo:
|
|
|
|
|
if not source_odoo and os.path.exists("./.erplibre-version"):
|
|
|
|
|
with open("./.erplibre-version") as f:
|
|
|
|
|
source_odoo = f.read()
|
|
|
|
|
if not source_odoo:
|
|
|
|
|
_logger.error(
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
"You cannot execute Odoo command if no version is"
|
|
|
|
|
f" installed. Command : {redact_secrets(command)}"
|
2026-02-13 01:27:38 -05:00
|
|
|
)
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
# Return the SAME shape the caller asked for. A bare int here
|
|
|
|
|
# made callers doing « status, cmd = exec_command_live(...) »
|
|
|
|
|
# crash with ValueError instead of seeing the failure.
|
|
|
|
|
if return_status_and_output_and_command:
|
|
|
|
|
return 1, command, []
|
|
|
|
|
if return_status_and_command:
|
|
|
|
|
return 1, command
|
|
|
|
|
if return_status_and_output:
|
|
|
|
|
return 1, []
|
|
|
|
|
return 1
|
2026-03-10 04:07:38 -04:00
|
|
|
command = f"source ./.venv.{source_odoo}/bin/activate && {command}"
|
2026-02-13 04:07:02 -05:00
|
|
|
if new_window and self.cmd_source_default:
|
2026-03-10 03:06:07 -04:00
|
|
|
command = self.cmd_source_default % command
|
2026-02-13 01:27:38 -05:00
|
|
|
|
|
|
|
|
if not quiet:
|
2026-03-12 05:20:32 -04:00
|
|
|
print("🏠 ⬇ Execute command :\n")
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
print(redact_secrets(command))
|
2026-03-10 03:06:07 -04:00
|
|
|
output_lines = []
|
2026-02-13 01:27:38 -05:00
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
process = subprocess.Popen(
|
2026-03-10 03:06:07 -04:00
|
|
|
command,
|
2026-02-13 01:27:38 -05:00
|
|
|
shell=True,
|
|
|
|
|
executable="/bin/bash",
|
|
|
|
|
stdout=subprocess.PIPE,
|
|
|
|
|
stderr=subprocess.STDOUT,
|
|
|
|
|
text=True,
|
2026-03-10 03:45:11 -04:00
|
|
|
bufsize=1, # Disable buffering for live output
|
|
|
|
|
universal_newlines=True, # Handle line breaks correctly
|
2026-02-13 01:27:38 -05:00
|
|
|
env=my_env,
|
|
|
|
|
)
|
|
|
|
|
|
[ADD] migration: keep the logs on disk, one file per step
The state screen showed « no tool has run yet » after closing and
reopening. It was reading the progression file, and that file is archived
and reset when a migration restarts — so everything it knew vanished at
the exact moment one wants to understand why the restart was needed.
Each step now has its own log file, appended never overwritten, and the
failures and tool verdicts go to an append-only JSONL beside them. Both
outlive the progression, and the screen reads disk first, memory second,
without counting the overlap twice.
The command output itself is captured where every line already passes,
in the executor's read loop: the terminal still shows it live and nothing
about the run changes. What goes through the real terminal cannot be
captured — a pipe there makes full screens refuse, that lesson is paid —
so those keep at least their command and their exit code.
--- FR ---
[ADD] migration : garder les journaux sur disque, un fichier par étape
L'écran d'état affichait « aucun outil n'a encore tourné » après une
fermeture. Il lisait le fichier de progression, or celui-ci est archivé
puis remis à zéro quand on recommence : tout ce qu'il savait disparaissait
au moment précis où l'on cherche pourquoi il a fallu recommencer.
Chaque étape a désormais son fichier, en ajout et jamais en écrasement, et
les échecs comme les verdicts d'outils vont dans un JSONL à côté. Les deux
survivent à la progression, et l'écran lit le disque d'abord.
La sortie des commandes est captée là où chaque ligne passe déjà, dans la
boucle de lecture de l'exécuteur : le terminal la montre toujours en
direct. Ce qui passe par le vrai terminal n'est pas captable — un tube y
ferait renoncer les pleins écrans — et garde au moins son verdict.
Assisted-by: Claude Opus 5
2026-08-19 04:32:25 -04:00
|
|
|
sink = getattr(self, "log_sink", None)
|
2026-02-13 01:27:38 -05:00
|
|
|
while True:
|
2026-03-10 03:06:07 -04:00
|
|
|
line = process.stdout.readline()
|
|
|
|
|
if not line:
|
2026-02-13 01:27:38 -05:00
|
|
|
break
|
|
|
|
|
if not quiet:
|
2026-03-10 03:06:07 -04:00
|
|
|
print(line, end="")
|
[ADD] migration: keep the logs on disk, one file per step
The state screen showed « no tool has run yet » after closing and
reopening. It was reading the progression file, and that file is archived
and reset when a migration restarts — so everything it knew vanished at
the exact moment one wants to understand why the restart was needed.
Each step now has its own log file, appended never overwritten, and the
failures and tool verdicts go to an append-only JSONL beside them. Both
outlive the progression, and the screen reads disk first, memory second,
without counting the overlap twice.
The command output itself is captured where every line already passes,
in the executor's read loop: the terminal still shows it live and nothing
about the run changes. What goes through the real terminal cannot be
captured — a pipe there makes full screens refuse, that lesson is paid —
so those keep at least their command and their exit code.
--- FR ---
[ADD] migration : garder les journaux sur disque, un fichier par étape
L'écran d'état affichait « aucun outil n'a encore tourné » après une
fermeture. Il lisait le fichier de progression, or celui-ci est archivé
puis remis à zéro quand on recommence : tout ce qu'il savait disparaissait
au moment précis où l'on cherche pourquoi il a fallu recommencer.
Chaque étape a désormais son fichier, en ajout et jamais en écrasement, et
les échecs comme les verdicts d'outils vont dans un JSONL à côté. Les deux
survivent à la progression, et l'écran lit le disque d'abord.
La sortie des commandes est captée là où chaque ligne passe déjà, dans la
boucle de lecture de l'exécuteur : le terminal la montre toujours en
direct. Ce qui passe par le vrai terminal n'est pas captable — un tube y
ferait renoncer les pleins écrans — et garde au moins son verdict.
Assisted-by: Claude Opus 5
2026-08-19 04:32:25 -04:00
|
|
|
if sink:
|
|
|
|
|
# Chaque ligne passe DÉJÀ ici : c'est le seul endroit où
|
|
|
|
|
# journaliser sans rien changer à ce que le terminal
|
|
|
|
|
# montre. Une erreur d'écriture ne doit jamais faire
|
|
|
|
|
# échouer la commande qu'on est en train de suivre.
|
|
|
|
|
try:
|
|
|
|
|
sink.write(line)
|
|
|
|
|
except Exception:
|
|
|
|
|
sink = None
|
2026-02-13 01:27:38 -05:00
|
|
|
if (
|
|
|
|
|
return_status_and_output
|
|
|
|
|
or return_status_and_output_and_command
|
|
|
|
|
):
|
2026-02-13 03:46:27 -05:00
|
|
|
# Remove last \n char
|
2026-03-10 03:06:07 -04:00
|
|
|
output_lines.append(
|
|
|
|
|
line.removesuffix("\r\n")
|
2026-02-13 03:46:27 -05:00
|
|
|
.removesuffix("\n")
|
|
|
|
|
.removesuffix("\r")
|
|
|
|
|
)
|
2026-02-13 01:27:38 -05:00
|
|
|
|
2026-03-10 03:45:11 -04:00
|
|
|
process.wait()
|
2026-03-10 03:06:07 -04:00
|
|
|
exit_code = process.returncode
|
2026-02-13 01:27:38 -05:00
|
|
|
if process.returncode != 0 and not quiet:
|
2026-03-10 04:07:38 -04:00
|
|
|
print("Command returned error code:" f" {process.returncode}")
|
2026-02-13 01:27:38 -05:00
|
|
|
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
# An exception MUST report a failure. exit_code stays None otherwise,
|
|
|
|
|
# and None is falsy: callers testing « if not status: » would mark the
|
|
|
|
|
# step as done, and « if status and wait_at_error » would skip the error
|
|
|
|
|
# prompt. A crashed command was therefore recorded as a success.
|
2026-02-13 01:27:38 -05:00
|
|
|
except FileNotFoundError:
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
exit_code = 1
|
2026-02-13 01:27:38 -05:00
|
|
|
if not quiet:
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
print(f"Error: Command '{redact_secrets(command)}' not found.")
|
2026-02-13 01:27:38 -05:00
|
|
|
except Exception as e:
|
[ADD] migration: see and repair the website COW views
A copy-on-write view freezes the module view it came from; the module moves
on and the upgrade dies hours later on a missing anchor. These tools
predict, snapshot, diff, neutralize and reset them. The migration screen
gains the real state of each step, replay from any of them, and statistics.
--- FR ---
Une vue copy-on-write fige la vue de module dont elle vient ; le module
évolue et la mise à niveau meurt des heures plus tard sur un point
d'ancrage absent. Ces outils les prévoient, photographient, comparent,
neutralisent et réinitialisent. L'écran de migration gagne l'état réel de
chaque étape, la reprise depuis n'importe laquelle, et des statistiques.
Assisted-by: Claude Opus 5
2026-08-10 03:10:50 -04:00
|
|
|
exit_code = 1
|
2026-02-13 01:27:38 -05:00
|
|
|
if not quiet:
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
print(f"An error occurred: {redact_secrets(str(e))}")
|
2026-02-13 01:27:38 -05:00
|
|
|
process_end_time = time.time()
|
|
|
|
|
duration_sec = process_end_time - process_start_time
|
|
|
|
|
if humanize:
|
|
|
|
|
duration_delta = datetime.timedelta(seconds=duration_sec)
|
2026-03-10 03:45:11 -04:00
|
|
|
human_time = humanize.precisedelta(duration_delta)
|
2026-02-13 01:27:38 -05:00
|
|
|
if not quiet:
|
2026-03-12 05:20:32 -04:00
|
|
|
print(f"🏠 ⬆ Executed ({human_time}) :\n")
|
2026-02-13 01:27:38 -05:00
|
|
|
else:
|
|
|
|
|
if not quiet:
|
2026-03-12 05:20:32 -04:00
|
|
|
print(f"🏠 ⬆ Executed ({duration_sec:.2f} sec.) :\n")
|
2026-02-13 01:27:38 -05:00
|
|
|
if not quiet:
|
[FIX] execute: redact the secrets before printing a command
CodeQL flagged five clear-text logging alerts here, and they are real:
todo.py and kdbx_manager.py build « --default_password_auth '<KeePass
password>' » and db_restore.py « --master_password=… », while this file
printed the command before and after every run and logged it on error. The
« if "password" in command » guard covered one branch out of six.
Redaction sits at the display point, not at construction: six outputs here
against commands built all over the repository. Only the value goes, never
the option name, and the RETURNED command stays clear — « [1] redo the
command » needs it. Verified that PreferredAuthentications=password survives.
--- FR ---
CodeQL signalait ici cinq journalisations en clair, et elles sont réelles :
todo.py et kdbx_manager.py construisent « --default_password_auth '<mot de
passe KeePass>' » et db_restore.py « --master_password=… », tandis que ce
fichier affichait la commande avant et après chaque exécution et la
journalisait en erreur. Le garde « if "password" in command » couvrait une
branche sur six.
Le caviardage est au point d'affichage, non à la construction : six sorties
ici, contre des commandes bâties partout dans le dépôt. Seule la valeur
part, jamais le nom de l'option, et la commande RENVOYÉE reste en clair —
« [1] refaire la commande » en dépend. Vérifié que
PreferredAuthentications=password reste intact.
Assisted-by: Claude Opus 5
2026-08-07 03:35:20 -04:00
|
|
|
print(redact_secrets(command))
|
2026-02-13 01:27:38 -05:00
|
|
|
print()
|
|
|
|
|
if return_status_and_output_and_command:
|
2026-03-10 03:06:07 -04:00
|
|
|
return exit_code, command, output_lines
|
2026-02-13 01:27:38 -05:00
|
|
|
if return_status_and_command:
|
2026-03-10 03:06:07 -04:00
|
|
|
return exit_code, command
|
2026-02-13 01:27:38 -05:00
|
|
|
if return_status_and_output:
|
2026-03-10 03:06:07 -04:00
|
|
|
return exit_code, output_lines
|
|
|
|
|
return exit_code
|