feat(lib): helper ini_set idempotent pour merge de fichiers INI #24

Closed
Damien wants to merge 3 commits from feat/lib-ini-set into main
3 Commits
Author SHA1 Message Date
Damien 1217b26ff9 fix(lib): ini_set n'écrase plus app.ini si awk échoue, insertion avant les lignes vides
Deux corrections dans ini_set() suite à la review de #24 :

- Le mv s'exécutait même si awk avait échoué en cours de route (OOM,
  signal, valeur exotique) : $tmp, créé par la redirection avant
  qu'awk ne tourne, pouvait être tronqué ou vide et venait quand même
  écraser le fichier original. mv est maintenant gaté sur le code de
  retour d'awk : en cas d'échec, $tmp est supprimé, une erreur est
  logguée, et la fonction retourne 1 sans toucher au fichier d'origine.

- L'insertion d'une clé juste avant l'en-tête de section suivante
  atterrissait après la ou les lignes vides de fin de section plutôt
  qu'avant, rendant le fichier plus difficile à lire au fil des appels
  répétés. Les lignes vides rencontrées section active/clé pas encore
  trouvée sont désormais bufferisées et réémises juste après la clé
  insérée (ou en fin de fichier si la section n'est jamais refermée),
  sans toucher à l'ordre dans tous les autres cas.
2026-08-01 11:18:48 +02:00
Damien 905166d1cc fix(lib): preserve file mode/ownership across ini_set's atomic write
mktemp defaults to 0600 root:root. The tmpfile+mv swap in ini_set was
carrying that over onto the replaced config, silently locking out
whatever service account owned the original file (e.g. gitea:www-data
on Gitea's app.ini — the service failed to start with a permission
denied on its own config after the very first ini_set call). Restore
the original file's mode and ownership on the tmpfile before the mv.

Verified on both BusyBox (Alpine) and GNU coreutils (Debian) stat -c.

Refs #18
2026-07-31 18:46:33 +02:00
Damien 66f981af81 feat(lib): add ini_set idempotent INI merge helper
openbao's "write config only if absent" pattern would silently skip
newly-required keys on a rejoué script. ini_set() merges key by key
instead: creates file/section/key as needed, replaces an existing
key's value in place, no-ops when already correct, and never touches
other sections — so a duplicate key name across sections (e.g.
[metrics] ENABLED vs [actions] ENABLED) stays scoped correctly.
Atomic write via tmpfile + mv. First OS-agnostic helper in the file,
documented as such in the header contract.

Refs #18
2026-07-31 17:34:45 +02:00