Commit graph

1122 commits

Author SHA1 Message Date
6d3a4c38cb [IMP] qemu: RAM presets up to 256G, shown with their GB equivalent
The presets stopped at 32768 MB; current virtualization hosts go well beyond
that. The series now doubles up to 262144 MB (256G).

The prompt is in MB while people think in GB, so each preset carries its
equivalent: « [g] 65536 (64G) ». With nine of them the line no longer fits, so
suggestions are laid out five per row. Disk presets keep the plain format.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 05:08:01 -04:00
2716127bff [IMP] qemu: offer lettered presets on the disk size and RAM prompts
Both prompts expected a raw number, so common values had to be retyped every
time. They now list suggestions:

    [a] 20G  [b] 40G  [c] 60G  [d] 80G  [e] 120G  [f] 200G
  New disk size in G, blank = keep (20G):

Letters start at « a » so they can never collide with a value typed directly:
anything starting with a digit is read as the value itself, and blank still
keeps the current one. A letter outside the range is rejected instead of being
silently taken for a size.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 04:59:02 -04:00
5f3afa6da2 [IMP] qemu: generate an SSH key during host setup when none exists
_qemu_default_ssh_key() returns '' when the user has no public key, so the
deployment injects none through cloud-init: the VM boots with no SSH access and
its state can no longer be checked once created.

--setup-host now generates an ed25519 key without passphrase when neither
id_ed25519.pub nor id_rsa.pub is present, so the Deployment profile leaves a
host able to reach the VMs it creates.

The key is created AS the invoking user via « sudo -u », not as root: under
sudo « ~ » is /root and the key would land where cloud-init never looks.

Verified on a fresh Arch VM with no key: it is created as erplibre:erplibre
with 700/600/644, a rerun keeps the same fingerprint, and
_qemu_default_ssh_key() now returns it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 04:49:03 -04:00
11d9b88de9 [IMP] migration: replay a single version bump, and stop offering a doomed retry
When OpenUpgrade failed, todo_upgrade_execute offered the generic
« [1] to redo the command ». Replaying it re-ran OpenUpgrade on the database it
had just half migrated, which never recovers. That call now passes
wait_at_error=False, and the failure message explains that the clone step has
been reset so a relaunch DROPS and REBUILDS the intermediate database from the
previous version.

The resume menu gains « [4.N] »: replay the step 4 loop from one version bump.
Only the per-version lists are trimmed from that index, so earlier bumps stay
migrated and steps 0 to 3 are untouched. Resetting the clone entry is the whole
point: the intermediate database of a failed bump must be rebuilt, not upgraded
again. « [4] » still replays every bump.

The per-bump uninstall files were also read at step 1 only, for the source
version, so uninstall_module_list_odoo130_to_odoo140.txt existed in name but
was never read. The step 4 loop now reads the file of the bump it is about to
perform, merged with the answers already stored in the progression.

Verified against the real progression (13 and 14 migrated): the menu offers
13/14/15/16/17/18, and replaying from 14 turns every per-version list from
[True, True, False...] into [True, False, ...] while state_4_reach_open_upgrade
and steps 0-3 survive. A 13->14 list of 4 modules is read with its reasons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 04:46:17 -04:00
1f0b5c021f [FIX] migration 13->14: hand group_fiscal_year over to om_account_accountant
The 13.0 -> 14.0 data migration died on:

  duplicate key value violates unique constraint "res_groups_name_uniq"
  Key (category_id, name)=(9, Allow to define fiscal years of more or less
  than a year) already exists

In 13.0 that security group is declared by the core « account » module, so the
database holds it as account.group_fiscal_year. In 14.0 core account no longer
declares it and om_account_accountant (odoomates) does. Owning no XML id of its
own, the module tries to CREATE the group and collides with the existing row.

Renaming the XML id makes Odoo UPDATE the existing record instead. The record
id is untouched, so user assignments, access rights and record rules pointing
at the group survive -- on the reference database it holds none, but the fix
must not depend on that.

The fix hook also learns a « .sql » flavour, run through psql. The « .py »
flavour is piped into « odoo<target>-bin shell », which requires loading a
not-yet-migrated database with the TARGET version's registry -- precisely what
is failing at that point. A pure SQL fix needs no ORM and no registry.

Verified end to end on a throwaway copy of the stuck database: the statement
renames one row, a second run changes nothing, and the full 13->14 OpenUpgrade
then completes (« Modules loaded. », base at 14.0.1.3, zero
res_groups_name_uniq error).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 04:21:06 -04:00
74ee584ffa [FIX] qemu: reboot after the install upgrades the kernel, and fix virtinst cache
On a rolling distro every fresh development VM was born unable to create VMs.
« make install_os » runs a full system upgrade, the kernel package is replaced
and /lib/modules/<running kernel> disappears. modprobe bridge then fails and
libvirt cannot create virbr0, so the « default » network stays inactive and
virt-install dies on « network 'default' is not active » -- with every package
correctly installed. Measured twice on a freshly created Arch VM: booted on
7.1.3-arch1-3 at 05:44, upgraded to 7.1.5.arch1-2 at 05:46.

Only a reboot fixes it, and one is enough: the default network is already
flagged autostart, so libvirt brings it up by itself once the modules match.
--setup-host therefore gains --reboot-if-needed, used by the deployment
profile. The reboot is scheduled through systemd-run --on-active=5 rather than
issued immediately, otherwise it would kill the installer's SSH session and the
orchestrator would report a failure for a VM that actually succeeded. Without
the flag the behaviour is unchanged: explain and exit 1, which is what a
workstation wants.

Also fix « Error setting up logfile: No write access to
/var/tmp/erplibre-virtinst/virt-manager »: that path was shared, so a first run
under sudo created it as root and later non-root runs could not write. It is
now per-UID.

Verified on the Arch VM that failed: without the flag it exits 1 with the
diagnosis; with it, the reboot is scheduled and the command still returns 0;
after the reboot the kernel and modules match, « default » is active on its own
and virbr0 exists. The cache directory is created as erplibre:erplibre 0700.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 04:00:21 -04:00
fba3b8aed1 [FIX] qemu: really prepare the host in the Deployment profile
The « ERPLibre Deployment (+ QEMU + dev) » profile installed packages with a
one-liner ending in « || true » and hiding stderr, so a broken host looked
installed. Reproduced on erplibre-arch-latest: every binary was present, yet
virt-install failed and « virsh list --all » could not reach any hypervisor.

Three distinct causes, all unhandled:

- The user was never added to the libvirt group. Without it a non-root libvirt
  client falls back to qemu:///session, where the « default » network does not
  exist, so « --network network=default » fails while everything looks
  installed. Being in the group grants the RIGHT to reach qemu:///system but
  does NOT change the default URI, so virt-install and virsh now pass
  « --connect qemu:///system » explicitly (new LIBVIRT_URI constant).
- dnsmasq was missing: ensure_tools only installed DAEMON_PACKAGES when the
  daemon was absent, and libvirt was already there. setup_host now forces them.
- On a rolling distro, « make install_os » upgrades the kernel and the package
  manager removes /lib/modules/<running kernel>. modprobe bridge then fails and
  libvirt cannot create virbr0 (« Unable to create bridge virbr0: Package not
  installed »). No package fixes that, only a reboot: it is now diagnosed and
  reported instead of surfacing as an unreadable virsh error.

The profile now calls « deploy_qemu.py --setup-host », which reuses the
existing ensure_* chain, so package names stay defined in one place
(TOOL_PACKAGES / DAEMON_PACKAGES) and keep working for apt, dnf, pacman,
zypper and brew. It fails loudly instead of « || true ».

Verified on the Arch VM that failed: setup-host reports the stale kernel and
exits 1; after a reboot it installs dnsmasq, joins libvirt/kvm, starts the
default network, and reports « Hôte prêt » (exit 0, idempotent on rerun).
The generated command now reads « virt-install --connect qemu:///system ».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 03:15:46 -04:00
a9ef5a0e8e [IMP] migration: show the real state of each step and replay from any of them
The resume menu exposed internal key names: « Reuse database without state_4 »
tells nobody what will happen, and the most useful option -- keep the prepared
database, redo the version bumps -- was the least readable of the four.

It now prints where the migration actually stands: the zip, the database, the
target, and one line per step with its state. Step 4 reports how many version
bumps are migrated and names them, so « 0/6 · 13 14 15 16 17 18 » replaces a
list index nobody could interpret.

The four numbered options become:
  [c]   continue where it stopped        (was: press enter)
  [0-4] replay from that step            (new: rewind to any step)
  [n]   new migration, erase everything  (was: [1])
  [r]   keep the zip, ask everything     (was: [4])

Rewinding drops « state_* » from the chosen step onward and keeps the rest:
config_* answers, the zip and the target version are decisions, not progress,
so they are no longer re-asked. state_0_search_missing_module is still forced
back to False, as the previous code did: it fills an in-memory dict the later
steps rely on. Old [2] is now « replay from 0 », old [3] « replay from 4 ».

First use of t() in this file; the new strings are translated in todo_i18n.py.

Verified against the real progression of the 12.0 -> 18.0 migration and a
synthetic one: for every step, the kept keys are exactly those of the earlier
steps, config and target survive, and the module search is forced again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 02:53:17 -04:00
96cc39431c [IMP] qemu: disable automatic upgrades on development VMs only
Measured on erplibre-ubuntu-2404: unattended-upgrades fired in the middle of
an Odoo 12->13 migration and restarted the PostgreSQL cluster three times
(« received fast shutdown request »). OpenUpgrade lost its connection and the
intermediate database was left half migrated.

On a development VM the ERPLibre installer now turns off unattended-upgrades
and the apt-daily timers, and drops an apt.conf.d snippet so they stay off
across reboots. dnf-automatic gets the same treatment on Fedora. It runs right
after the cloud-init wait and before the apt-get calls, so apt-daily can no
longer grab the lock between the two either -- the same contention that made
« apt-get update » fail during deployment.

Production VMs are left untouched: automatic security updates must stay on
there. The switch is the existing dev/prod answer, already threaded down to
_qemu_erplibre_remote_cmd.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 02:42:45 -04:00
79a07ac577 [FIX] migration: rebuild the clone when the data migration fails
When OpenUpgrade fails, the intermediate <db>_upgrade_<N> database is left
half migrated. The clone step had already been recorded as done, so a rerun
skipped it and restarted OpenUpgrade on top of that broken clone.

Clear the clone flag on failure so the replay rebuilds the intermediate
database from the pristine source. Found by a real 12.0 -> 13.0 run: Ubuntu
unattended-upgrades restarted the PostgreSQL cluster mid-migration, Odoo lost
its connection, and the replay would have resumed on the damaged clone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 02:38:54 -04:00
8ce4b93292 [ADD] migration: neutralize the COW views that would break a version bump
key is the only thing pairing a website copy with the module view it came
from. A copy whose key matches nothing is never paired, so it never receives
the new inherit_id, never changes shape, and never joins a view combination.
Renaming the key is therefore enough to take it out of the way:

    UPDATE ir_ui_view SET key = '<prefix>.' || key, active = false

active = false alone would NOT work: an inactive copy keeping the same key
still shadows the module view. Nothing is deleted either, so inherit_id
ondelete='restrict' and the website_page foreign keys are never touched, and
the old arch stays in database as a readable archive. --restore undoes it.

Written in plain psql on purpose: it runs on a database not yet migrated,
where starting an Odoo shell of the target version is not guaranteed. It is a
systematic step driven by the detector, valid for every bump, so it lives in
the loop rather than in a per-version fix_migration file.

Offered rather than forced, since those copies carry real customizations.

Verified on a throwaway copy: dry-run writes nothing; apply renames the key and
deactivates while keeping the 4058-char arch; a second apply is a no-op;
restore returns the row to its exact initial state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 01:04:38 -04:00
1e4cc4f471 [ADD] migration: snapshot and diff the website COW views at each version bump
A version bump rewrites website views without announcing any of it, so "the
site looks wrong" after a migration is currently unanswerable.

snapshot_cow_views.py records every website_id view (key, mode, inherit_id,
active, arch and its md5) and diffs two snapshots. Rows come back as JSON
straight from Postgres because an arch holds newlines and pipes; the column
list is intersected with information_schema, since ir_ui_view does not expose
the same columns from 12.0 to 18.0. Snapshots hold customer template content,
so they go under private/ and stay out of git.

todo_upgrade.py takes one before and one after each OpenUpgrade run, then
prints the diff. Both are non-blocking: forensic material must never stop an
upgrade.

Measured on the real 12.0 -> 13.0 jump, the diff shows what no log reported:
71 -> 68 copies, 16 deleted and 13 created. portal.frontend_layout is not
converted but DELETED (id 2670) and RECREATED (id 3397), with its children
re-parented from one to the other. The four theme_technolibre copies,
muk_web_branding, project_agile and erplibre_website_snippets_basic_html are
dropped outright, and website_crm.contactus_thanks is renamed to
website_form.contactus_thanks.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 00:58:54 -04:00
1ec37defc3 [FIX] check_cow_views: detect on arch shape, not on mode
Two blind spots made the detector miss real breakages.

1. Comparing « mode » is not the right test. What decides the shape an arch
   must have is whether the target declares an inherit_id: with one, the arch
   must be inheritance specs (<data>, <xpath>, position=); without one, it must
   be a standalone template. A view moving from a root template to
   « inherit_id + primary="True" » keeps mode='primary' on BOTH sides, so the
   old test reported nothing while the copy still broke. The comparison is now
   (target declares inherit) vs (stored arch is spec-shaped), and each finding
   carries the reason. A mode change with a matching shape is still reported,
   as a lesser warning.

   arch_db is read as text up to 15.0 and as jsonb from 16.0; both are handled.

2. A module renamed upstream was reported as « module absent », hiding every
   view it owns. renamed_modules is now read from the target OpenUpgrade
   apriori.py (21 entries for 13.0, 56 for 14.0, 39 for 16.0, 20 for 18.0) and
   used before concluding the module is gone.

Also correct the advice printed for a copy at risk: deactivating it is not
enough, an inactive copy keeping the same key still shadows the module view.
Renaming the key is what actually unpairs it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-01 00:54:44 -04:00
045ccc1c85 [ADD] migration: predict the website COW views a version bump will break
A copy-on-write view freezes the structure of the module view it was copied
from. When that module view changes mode between two versions, the copy keeps
an arch written for the old mode and the upgrade dies on
« Element ... cannot be located in parent view », hours into the migration.

Measured on a real 12.0 database: portal.frontend_layout is declared primary in
12.0 (full QWeb template, so a full-template arch is legitimate) and becomes an
extension in 13.0 (inherit_id + xpath). The 2021 COW copy follows the module,
turns into an extension, and keeps its 12.0 arch. The customization is genuine;
the breakage is produced by the migration.

check_cow_views.py compares the mode stored in database with the mode declared
in the target version sources and sorts the views in three buckets: at risk
(mode change), module absent from the target version, and pages made from the
editor (not at risk). Read-only, and called from step 2 so the list is known
before the long version loop rather than during it.

On the reference database it reports exactly the view that broke the upgrade,
plus 7 views whose module is gone in 13.0 (custom theme included).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 23:28:54 -04:00
4536c5bc40 [IMP] migration: per-database uninstall list, with a stated reason
Which modules must be uninstalled before a version bump depends on the data of
one specific database, so that list does not belong to a shared versioned file.

- Read private/odoo/migration/<database>/uninstall_module_list_odooXX0_to_odooYY0.txt
  first, then the shared versioned defaults under script/odoo/migration/, and
  merge them (duplicates dropped). private/ mirrors script/, the convention
  already used by script/todo/todo.json -> private/todo/todo_override.json.
- Fix the parser. It was « f.readline().split() »: only the FIRST line was kept,
  so a multi-line list was silently truncated, and a comma-separated list turned
  into one bogus module name. On a file starting with a comment it returned the
  words of that comment as module names. It now reads every line and accepts
  commas, several names per line, blank lines and comments.
- Support a « # reason » justification per module, printed before uninstalling;
  a module with no reason is flagged. Removing a module must stay reviewable.
- Ignore private/odoo/ in git: these lists describe one database.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 23:25:43 -04:00
ab9f94cc28 [FIX] migration: stop recording failed steps as successful
The database migration recorded steps as done even when they had failed, so a
resume skipped them and the loop kept going on a broken database.

execute.py
- exec_command_live() left exit_code = None when an exception was raised. None
  is falsy, so « if not status: » marked the step done and
  « if status and wait_at_error » skipped the error prompt: a crashed command
  was reported as a success. Both except blocks now set exit_code = 1.
- The « no Odoo version installed » path returned a bare -1 while callers
  unpack a tuple (status, cmd), raising ValueError instead of surfacing the
  failure. It now returns the same shape the caller asked for.

todo_upgrade.py
- todo_upgrade_execute(): treat a None status as a failure (defence in depth).
- Database migration (OpenUpgrade): the return code was discarded, with an
  explicit « TODO detect error », and the state was written unconditionally.
  It is now captured; on failure the loop stops instead of migrating the next
  version on top of a half-migrated database.
- Neutralization: the flag was set before the « if not status » test, making
  that test dead code. Removed the unconditional assignment.
- Clone and fix-migration hook: their return codes were never captured, so the
  steps were marked done whatever happened. Both are now checked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 23:22:02 -04:00
b8f2bd7343 [FIX] update_manifest_local_dev: git daemon doublon -> exit 1
A leftover git daemon from an interrupted run kept port 9418, so the new
daemon failed to bind ("Address already in use") and the final « kill
$DAEMON_PID » failed on the already-dead PID, returning exit 1. That non-zero
status bubbled up through apply_extra_modules() and set exit_code=1, which
silently skipped the post-install steps (add_extra_to_config_conf,
generate_config) -> CybroOdoo never landed in config.conf.

- Kill any leftover daemon before starting a new one. Match the stable
  arguments, not "git daemon": « git daemon » execs into « git-daemon »
  (hyphen, /usr/lib/git-core/git-daemon), so a "git daemon" (space) pattern
  never matched the actual process.
- Move daemon cleanup into an EXIT trap that tolerates an already-dead PID,
  so the script never exits non-zero just because the daemon is already gone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 01:59:36 -04:00
a6ecc8aa3c [FIX] update_env_version: --with_extra clone CybroOdoo + config.conf
The extra modules (CybroOdoo) were still not applied on an already-installed
environment even after apply_extra_modules() was added. Two root causes:

1. When update_env_version.py runs as a script, sys.path[0] is
   script/version/, so « from script.version.erplibre_state import ... »
   raised ImportError and _STATE_AVAILABLE fell back to False. The whole
   state mechanism was silently disabled: set_version_installed() never
   wrote .erplibre-state.json, get_version_extra() stayed False, and the
   extra manifest was never merged -> CybroOdoo never cloned. Add a sibling
   import fallback so the state module also resolves under script execution.

2. generate_config.sh rewrites config.conf from a fixed addons list that
   omits the extra modules. Add add_extra_to_config_conf(), called AFTER
   generate_config.sh (last writer wins), which appends the extra addons
   paths (derived from the "extra" group of the extra manifest) to
   addons_path. Idempotent: already-present paths are skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 01:18:14 -04:00
71aba63b03 [FIX] install: --with_extra sans effet sur un env déjà installé (CybroOdoo)
« Installation avec modules extra » (--with_extra) ne clonait pas CybroOdoo
sur un env déjà installé : validate_environment renvoyait « valide » ->
update_environment (où l'état extra est posé via set_version_installed) était
sauté -> get_version_extra restait False -> git_merge_repo_manifest n'ajoutait
pas manifest/git_manifest_extra_odooXX.xml -> CybroOdoo jamais synchronisé ni
ajouté à config.conf (« Nothing to do »).

Nouvelle étape apply_extra_modules(), appelée dans main() quand --with_extra
est demandé (même si l'env de base est déjà installé) : pose l'état extra,
régénère le manifest local + repo sync (clone CybroOdoo). La post-étape qui
suit régénère config.conf, qui prend alors le nouveau chemin d'addons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 01:02:05 -04:00
5951b0374f [UPD] manifest odoo 12: update repo OCA_queue 2026-07-31 01:01:11 -04:00
d84d729c77 [UPD] script todo upgrade: ignore update all state 2 when done in state 1 2026-07-31 00:34:32 -04:00
52492302d3 [IMP] script todo: sshfs -o follow_symlinks (git sur dépôts google-repo)
Le montage sshfs de « Configurer sshfs » ajoute désormais
« -o follow_symlinks » : sshfs résout les symlinks côté serveur. Sans ça,
git échoue sur les worktrees google-repo d'ERPLibre (leur .git est une
chaîne de symlinks relatifs profonds vers .repo/projects et project-objects)
-> « erreur à la lecture de .git », git status/commit impossibles sur le
montage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-31 00:07:36 -04:00
db1e9fa24f [FIX] télémétrie navigation : extraire les commandes des menus « choices »
« Code » (et Update, Git, Database…) apparaissait vide : l'extracteur AST ne
comprenait que le motif « status == "N": self.methode() » (menu QEMU). Les
menus bâtis par « choices = config_file.get_config(...) » + « choices.append(
...) » avec dispatch « str(len(choices)-N) » n'exposaient aucun enfant ->
impossible d'ouvrir leurs commandes.

Ajout de l'extraction de ce motif :
- entrées de CONFIG lues depuis todo.json (rejouées via
  execute_from_configuration avec l'entrée en kwargs) ;
- entrées APPENDÉES (dict littéral OU variable menu_entry=… suivie par n° de
  ligne) mappées à leur méthode via le dispatch « str(len(choices)-K) » ;
- une entrée qui ouvre un sous-menu (ex. Update) est développée récursivement.

Validé : Code -> 7 commandes (statut/remiser/formater/SHELL/màj module/débogage
+ sous-menu Update), méthodes correctes et existantes, TUI monte sans erreur.
Les dispatches complexes (ex. « Migration BD » via TodoUpgrade) restent
visibles mais non lançables (method None).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 23:24:40 -04:00
839ef08ea4 [IMP] script todo: icônes menus Code et Update
Icônes ajoutées aux valeurs i18n :
- Code : 🔍 statut, 📦 remiser, 🎨 formater, 🐚 SHELL, 🧩 mise à jour module,
  🐛 débogage.
- Update : 🔄 erplibre_base (test), 🚚 migration BD, 📦 dépendances Poetry.

Emojis larges -> 1 espace, alignement préservé. Valeurs i18n uniquement.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 23:20:49 -04:00
a2b507c471 [IMP] script todo: déplacer « Mise à jour » du menu Execute vers Code
L'entrée « Update - Update all developed staging source code » quitte le
menu Execute (renumérotation 9->8 … 15->14, dispatch elif mis à jour) et
rejoint le sous-menu « Code - Outil pour développeur » (dernière entrée,
appelle prompt_execute_update). Le libellé/i18n (🔃) est réutilisé.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 23:20:49 -04:00
b856548d64 [IMP] script todo: migration Odoo update all before neutralize 2026-07-30 23:20:21 -04:00
671730b841 [FIX] selenium: retirer -remote-allow-system-access (refusé par geckodriver récent)
webdriver.Firefox échouait : « InvalidArgumentException: Argument
--remote-allow-system-access can't be set via capabilities ». Cet argument
(ajouté pour le Firefox snap d'Ubuntu) est désormais REFUSÉ via les
capabilities par les geckodriver récents, qui gèrent seuls l'accès système
du snap. Il cassait donc TOUS les setups sur geckodriver récent (ici Arch
sans snap). On ne le passe plus.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 15:15:32 -04:00
06687f5f7e [FIX] selenium: importer Optional (NameError sur l'annotation de type)
extraire_svg_graphique_by_3d annote « -> Optional[str] » mais le module
n'importait pas Optional -> « NameError: name 'Optional' is not defined »
au chargement. Ajout de « from typing import Optional ».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 15:11:12 -04:00
12d1c0c8ae [IMP] script todo: déploiement — personnaliser nom + disque + RAM par VM
« Renommer quelles VM ? » devient « Modifier quelles VM ? » : pour chaque
VM choisie, on peut changer le NOM, la taille de DISQUE et la RAM (vide =
garder). _qemu_customize_names -> _qemu_customize_vms renvoie (names,
selected) avec les valeurs mises à jour.

Le multiplicateur de ressources est désormais CUIT dans `selected` (RAM
finale) et le nombre de vCPU fixé une fois (uniforme) -> un override de RAM
par VM est une valeur ABSOLUE, sans ambiguïté avec le multiplicateur. La
fonction eff_res disparaît (plan, dry-run et déploiement lisent les valeurs
finales de `selected`).

Validé : modif VM 1 (nom/disque/RAM) appliquée, VM 2 inchangée.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 14:58:50 -04:00
6506e5cf9f [IMP] script todo: suivi install — colonnes État (8) et VM (22) plus étroites
- État : 10 -> 8. Le libellé «  effacée » (10 cellules) forçait la largeur ;
  l'état « effacée » devient l'icône seule 🗑. Reste le plus long « ⏸ pause »
  (7), donc 8 suffit.
- VM : 26 -> 22 (tient les noms standards « erplibre-ubuntu-2604 » = 20 ; les
  rares noms suffixés d'arch défilent via overflow-x).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 03:44:38 -04:00
19c5f41751 [FIX] script todo: service Odoo dev — SELinux permissif (unconfined inefficace)
« SELinuxContext=unconfined_u:unconfined_r:unconfined_t:s0 » ne débloque PAS
l'exécution : la transition init_t -> unconfined_t est refusée par la
politique -> le service échouait toujours en 203/EXEC (« Permission denied »
sur run.sh, contexte user_home_t) sur Fedora.

En DEV (VM jetable, « SELinux relâché ») on passe désormais SELinux en
PERMISSIF (setenforce 0 + persistance dans /etc/selinux/config) si actif ->
le service exécute run.sh/venv sous /home sans blocage.

PROD reste confiné (/opt/erplibre hors user_home_t + restorecon) — à
peaufiner quand on reviendra sur la prod.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 03:21:58 -04:00
233fdf2d2b [IMP] script todo: suivi install — colonne État moins large (12 -> 10)
La colonne « État » réservait 12 caractères alors que le plus long libellé
(«  effacée ») en fait 10 -> elle paraissait trop large. Ramenée à 10
(sans troncature).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 03:10:04 -04:00
e8c60a2d46 [FIX] install: apt lock — réessayer « update » (DPkg::Lock::Timeout insuffisant)
Le fix précédent (DPkg::Lock::Timeout) ne suffisait pas : cette option NE
couvre PAS le verrou /var/lib/apt/lists/lock de « apt-get update ». Au 1er
boot, cloud-init/apt-daily tient ce verrou -> update échouait AUSSITÔT
-> lists vides -> « Unable to locate package git » (exit 100), toujours sur
debian-12.

On RÉESSAIE désormais « apt-get update » jusqu'à libération du verrou (et
lists peuplées), borné à ~5 min (30×10s), avant l'install. Une fois update
OK, git/make s'installent normalement.

Note : le remote_cmd est construit côté HÔTE -> relancer le CLI todo pour
que la nouvelle commande soit envoyée aux VM.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 02:36:31 -04:00
bcb77df2d3 [IMP] script todo: suivi install — colonne # (séquence) + max de durée
- Nouvelle 1re colonne « # » : numéro de séquence de chaque VM.
- Sommaire enrichi du « max » de durée : la VM TERMINÉE la plus lente
  (pire cas), à côté du temps global et de l'ETA.

Validé headless : # = 1..N, sub_title « … · max MM:SS ».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 02:30:29 -04:00
808e37cd59 [IMP] script todo: déploiement — choix dev/prod (/opt + SELinux confiné en prod)
Au déploiement (« Déployer une ou plusieurs VM »), après le choix de la
branche, on demande l'ENVIRONNEMENT cible (dev par défaut / prod).

- DEV : ERPLibre dans ~/git/erplibre ; service systemd unconfined si
  SELinux actif (comportement inchangé).
- PROD : ERPLibre dans /opt/erplibre (sudo git clone + chown à
  l'utilisateur) ; service systemd CONFINÉ par SELinux (pas d'unconfined)
  + restorecon des contextes -> hors user_home_t, un service peut exécuter
  le contenu sans lever le confinement.

Le drapeau `prod` est propagé : _qemu_deploy -> _qemu_install_erplibre_
monitored/_vm -> _qemu_erplibre_remote_cmd -> _qemu_odoo_service_cmd.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 02:09:29 -04:00
43d268e448 [FIX] install: poetry — rejeu -vvv (et non -v) à l'échec pour la vraie cause
« -v » montre l'étape mais pas la sortie des sous-processus. Seul « -vvv »
(debug) affiche git clone/checkout et le build pip -> l'erreur réelle d'une
dépendance VCS/build. Le rejeu de diagnostic passe donc en -vvv.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:56:04 -04:00
64c0bbc698 [FIX] install: revue 13 VM — apt lock, wkhtmltopdf trixie, SELinux, poetry
Corrections issues de la revue complète des 13 VM (6 en échec réel) :

- APT lock (debian-12) : au 1er boot, cloud-init/apt-daily tient le verrou
  et « apt-get install » échouait aussitôt. Ajout de
  « -o DPkg::Lock::Timeout=600 » (update + install) -> attend le verrou.

- wkhtmltopdf Debian 13 « trixie » (debian-13) : on prenait le build
  « bullseye » (dépend de libssl1.1, absent de trixie) -> gdebi échouait.
  Désormais bookworm pour bookworm/trixie/+. ET l'échec gdebi est NON
  bloquant (avertissement au lieu d'exit 1 : wkhtmltopdf est optionnel,
  sinon tout install_os avortait et Odoo n'était jamais installé).

- SELinux 203/EXEC (fedora-41/43/44) : un service système ne peut pas
  exécuter run.sh/venv sous /home (contexte user_home_t). Ajout
  conditionnel de « SELinuxContext=unconfined_u:unconfined_r:unconfined_t:s0 »
  au service (uniquement si getenforce != Disabled).

- poetry status 1 (ubuntu-2604) : « -q » masquait la cause. À l'échec, on
  rejoue « poetry install -v » pour capturer l'erreur dans le log.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:52:55 -04:00
7904c72d71 [IMP] script todo: suivi install — colonne « Odoo » (état :8069)
Nouvelle colonne « Odoo » dans le dashboard : teste par TCP si l'UI web
répond sur le port 8069 de la VM (🟢 up / — sinon). Une fois « up »
détecté, on ne re-teste plus (Odoo ne redescend pas en cours d'install).
Le test (socket, timeout 0,5 s) est fait dans le thread collecteur ->
n'impacte pas la fluidité de l'UI.

Validé headless : port ouvert -> 🟢, fermé -> —.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:52:55 -04:00
1ebf2b353f [FIX] qemu: Fedora image robuste (miroirs) + silence du bruit virtinst
Déploiement PARALLÈLE de 13 VM -> 2 problèmes observés :

1. « Aucune image Fedora-Cloud-Base-Generic pour Fedora 42 » alors que
   l'image EXISTE. download.fedoraproject.org est un redirecteur
   (MirrorManager) : sous requêtes parallèles, une VM tombait sur un miroir
   incomplet -> index HTML sans correspondance -> sys.exit.
   resolve_fedora_url réessaie (2× le redirecteur) puis se replie sur le
   serveur MAÎTRE dl.fedoraproject.org (toujours complet). Validé : F41/F42
   résolus.

2. Pavé « Fetched capabilities … » (énorme XML) dans la sortie : erreur de
   LOGGING de virtinst — sous sudo, HOME/cache inaccessible -> échec
   d'écriture du journal debug -> Python déverse l'enregistrement raté.
   On préfixe virt-install de « env XDG_CACHE_HOME=… HOME=… » (traverse
   sudo) vers un cache écrivable -> journal silencieux.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:41:17 -04:00
b99db0ea66 [FIX] script todo: suivi install — navigation fluide (tail + debounce)
Cause des GROS ralentissements en changeant de VM / lisant les logs :
- on_data_table_row_highlighted rechargeait le log à CHAQUE mouvement du
  curseur (donc en rafale quand on tient la flèche) ;
- _load_selected_log(reset) lisait le fichier ENTIER (offset 0) et écrivait
  TOUTES les lignes dans le RichLog, SYNCHRONE sur la boucle d'événements
  -> un log de 250 Ko / plusieurs milliers de lignes gelait l'UI, × chaque
  pas de navigation.

Corrections :
- _read_tail : au changement de VM on ne lit que la FIN du log (128 Ko /
  1000 lignes max) et on cale l'offset sur la taille totale -> le suivi
  incrémental continue depuis la fin. Affichage quasi instantané.
- Debounce (0,25 s) : RowHighlighted mémorise seulement la cible ; on ne
  charge qu'une fois le curseur stabilisé, et uniquement la DERNIÈRE VM.

Validé headless : 2 flèches rapides -> 1 seul chargement (VM finale),
1000 lignes affichées au lieu de 8000.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:31:00 -04:00
fd3226a053 [REV] script todo: suivi install — revert auto-refresh coupé par défaut (4f0c3cd)
L'auto-refresh coupé par défaut était une mauvaise idée : (1) les lags
persistaient malgré tout ; (2) une fois coupé, on ne voyait plus
l'avancement (et l'activation ne suffisait pas). On revient au
comportement précédent : rafraîchissement automatique TOUJOURS actif
(logs 1s / table 2s / domstate 10s) — au moins on voit la progression.

Revert de 4f0c3cd. L'analyse du VRAI ralentissement (navigation / logs)
est traitée séparément.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:28:06 -04:00
bea9a82d75 [IMP] script todo: redimension — avertir de la taille max soutenable (hôte)
À l'agrandissement, on indique désormais l'espace libre de l'hôte et la
taille virtuelle MAX « soutenable » ≈ (taille réelle actuelle + libre
hôte) — affichée AVANT la saisie pour guider le choix. Si la cible la
dépasse, avertissement NON bloquant : le qcow2 est creux, donc OK tant que
la VM ne remplit pas, mais au-delà l'hôte tomberait à court d'espace.

Ex. : réel 115G + libre 30G -> max soutenable ~145G ; viser 190G affiche
« dépasse de ~45G — surallocation ». L'opération n'est pas bloquée.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:08:13 -04:00
ae3745a22a [FIX] qemu: forcer LC_ALL=C sur virsh/sgdisk/… (locale fr cassait l'état des VM)
Sur un hôte en français, « virsh domstate » renvoie « en cours d'exécution »
au lieu de « running ». Le test « state == "running" » échouait donc :
l'agrandissement à chaud tombait dans la branche « qemu-img resize » (au
lieu de « virsh blockresize ») sur une VM ALLUMÉE -> « Failed to get write
lock ».

On force désormais LC_ALL=C / LANG=C (via _qemu_c_env) sur tous les outils
dont on PARSE la sortie, pour avoir l'anglais quelle que soit la locale :
virsh domstate/domname/dominfo/domblklist/list, sgdisk -i, dumpe2fs,
resize2fs -P (todo.py) et virsh list --all (dashboard). Cela répare aussi,
en locale fr, la détection d'état (pause/éteinte), l'analyse des infos
avancées (CPU/RAM) et le parsing de la réduction (partition/GPT).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 01:04:07 -04:00
8be034f25e [IMP] script todo: réduction — sauvegarde optionnelle + effacement du backup
Trois améliorations autour de la sauvegarde de disque à la réduction :

- La sauvegarde .bak n'est plus systématique : on DEMANDE (défaut OUI)
  « Sauvegarder le disque avant réduction ? ». Si non, avertissement (un
  échec pourrait casser le disque, pas de restauration possible).
- À la FIN, après avoir proposé de démarrer la VM (donc après test manuel
  possible), on propose d'EFFACER la sauvegarde (défaut NON -> on la garde
  par prudence).
- « Nettoyer QEMU » détecte désormais les sauvegardes *.qcow2.bak (libellé
  « sauvegarde de disque (redim.) ») et permet de les effacer.

_qemu_shrink_revert gère l'absence de sauvegarde (avertit de lancer fsck
au lieu de restaurer). Validé de bout en bout sur image jetable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:48:42 -04:00
c9ec5b98b9 [FIX] script todo: réduction — détection de partition robuste (FSTYPE vide)
« Partition à réduire introuvable » sur une vraie VM : _qemu_root_part
parsait lsblk en positionnel et EXIGEAIT 4 colonnes. Juste après le connect
nbd, le FSTYPE n'est pas encore en cache -> colonne VIDE -> 3 tokens ->
toutes les partitions étaient ignorées. (Le test initial passait car le FS
avait eu le temps d'être détecté.)

- lsblk -P (paires clé="valeur") : robuste aux colonnes vides ; on repère
  la partition par TYPE="part" et on sonde le FSTYPE via blkid si absent.
- _qemu_nbd_connect attend l'APPARITION des sous-périphériques nbdNpM
  (jusqu'à ~15 s) avant de rendre la main.
- partprobe silencieux (capture) -> plus de spam « Invalid argument during
  seek » pendant la réparation GPT.

Validé sur image jetable au layout cloud (p1 root + p14 bios + p15 ESP),
détection immédiate après connect : 25G -> 15G, GPT « No problems found »,
partitions préservées, fsck propre.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:41:54 -04:00
545193d293 [FIX] qemu: disque minimum 20G (10G -> poetry « disk full » sur ubuntu-2004)
erplibre-ubuntu-2004 (déployée à 10G) plantait à l'install : « Poetry
installation error status 1 » car le disque était plein (9.7G/10G). Un
ERPLibre + Odoo occupe ~11G, plus le transitoire (caches pip, sync repo,
sources Odoo). Preuve : la VM à 15G réussissait, celle à 10G non.

Plancher relevé à 20G pour TOUTES les versions (ubuntu 20.04/22.04,
debian, fedora, arch). Le qcow2 est CREUX (sparse) : une taille virtuelle
plus grande ne consomme rien tant qu'elle n'est pas remplie -> aucun
surcoût réel, marge confortable pour l'installation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:41:54 -04:00
9fb24e583a [FIX] script todo: réduction disque via qemu-nbd (libguestfs cassé sur l'hôte)
La réduction « sûre » via virt-resize ne marchait pas : (1) j'utilisais
« virt-filesystems -b » (option INEXISTANTE -> aucune partition détectée ->
« abandon »), et (2) libguestfs est inutilisable ici (aucun vmlinuz dans
/boot -> l'appliance supermin échoue). La VM restait donc à 25G.

Réécriture SANS libguestfs, avec des outils de base présents et éprouvés
(qemu-nbd, e2fsck, resize2fs, sgdisk, parted/partprobe) :
1. copie .bak AVANT toute modification ;
2. nbd + détection de la racine (plus grosse partition, ext seulement) ;
3. e2fsck -> resize2fs (FS) -> sgdisk réécrit la partition en PRÉSERVANT
   type/UUID/nom (PARTUUID intact) -> qemu-img --shrink (conteneur) ->
   sgdisk -e (GPT de secours) -> fsck final ;
4. en cas d'échec à N'IMPORTE quelle étape : restauration depuis .bak
   -> corruption impossible.

Validé de bout en bout sur une image jetable (GPT + ext4, racine à offset
élevé, 800M de données) : 25G -> 15G, GPT « No problems found », fsck
propre, UUID préservé, données intactes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:34:45 -04:00
0b02e9fe43 [IMP] script todo: QEMU — rouvrir le suivi d'installation (dernier run / historique)
Si le dashboard se ferme (bug, sortie), on peut désormais le ROUVRIR sur un
run passé pour reprendre l'analyse. Nouvelle entrée [11] du menu QEMU :
« 📈 Rouvrir le suivi d'installation (dernier run / historique) ».

- mon.list_install_runs() : liste les runs (~/.erplibre/qemu-install/*/
  session.json) triés du plus récent au plus ancien.
- _qemu_reopen_monitor : affiche l'historique (VM par run), choix (défaut =
  le dernier), puis run_monitor(session.json) rouvre le dashboard.
- « Lister les images » passe de [11] à [12] ; config -> 13+.

Validé : 20 runs listés, dashboard reconstruit (titre + 5 VM) depuis un
session.json passé.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:22:37 -04:00
4f0c3cd217 [IMP] script todo: suivi install — auto-refresh coupé par défaut (moins de lag)
Sur un gros parc, les ticks périodiques (logs 1s / table 2s / domstate 10s)
causaient du lag à la lecture des logs. Nouveau bouton « a » pour couper/
activer le rafraîchissement automatique, COUPÉ par défaut ; « r » pour
rafraîchir une seule fois à la demande.

- Chaque tick est scindé en wrapper _tick_* (ne fait rien si auto coupé) +
  corps _do_* (le travail réel), réutilisable pour les rafraîchissements
  forcés (init à l'ouverture, toggle, refresh manuel, après pause/reprise).
- Un relevé initial unique (table + domstate) à l'ouverture affiche l'état
  courant même auto coupé ; changer de VM recharge son log (synchrone).
- Indicateur #autobar : « ⏸ Rafraîchissement auto : COUPÉ (a=activer ·
  r=rafraîchir) » / « ▶ … : ACTIVÉ (a=couper) ».

Validé headless : défaut OFF, toggle a, refresh r, table peuplée à l'init.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:14:58 -04:00
c60b3f5cbd [IMP] script todo: dashboard de suivi — défaut OUI
« Suivi interactif (dashboard) ? » répondait NON par défaut (réponse vide).
Le défaut est désormais OUI : nouveau helper _is_yes_default_yes (vide = oui)
et libellé « (O/n, défaut : oui) » / « (Y/n, default: yes) ».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 00:09:37 -04:00