life-noc/patch_bpm_native.sh

261 lines
7.7 KiB
Bash
Raw Normal View History

#!/usr/bin/env bash
set -euo pipefail
ROOT="${1:-.}"
need_file() {
local f="$1"
if [[ ! -f "$ROOT/$f" ]]; then
echo "Fichier introuvable: $f" >&2
exit 1
fi
}
echo "==> Vérification du dépôt"
need_file "domains.yaml"
need_file "scripts/generate_bpm.py"
need_file "Makefile"
need_file "ansible/roles/life_noc/defaults/main.yml"
need_file "ansible/roles/life_noc/tasks/main.yml"
echo "==> Sauvegarde minimale"
mkdir -p "$ROOT/.patch-backup"
cp -a "$ROOT/scripts/generate_bpm.py" "$ROOT/.patch-backup/generate_bpm.py.bak"
cp -a "$ROOT/Makefile" "$ROOT/.patch-backup/Makefile.bak"
cp -a "$ROOT/ansible/roles/life_noc/defaults/main.yml" "$ROOT/.patch-backup/life_noc_defaults_main.yml.bak"
cp -a "$ROOT/ansible/roles/life_noc/tasks/main.yml" "$ROOT/.patch-backup/life_noc_tasks_main.yml.bak"
[[ -f "$ROOT/preuve-de-reproductibilite.md" ]] && cp -a "$ROOT/preuve-de-reproductibilite.md" "$ROOT/.patch-backup/preuve-de-reproductibilite.md.bak"
[[ -f "$ROOT/decisions-techniques.md" ]] && cp -a "$ROOT/decisions-techniques.md" "$ROOT/.patch-backup/decisions-techniques.md.bak"
echo "==> Réécriture de scripts/generate_bpm.py"
cat > "$ROOT/scripts/generate_bpm.py" <<'PYEOF'
#!/usr/bin/env python3
from __future__ import annotations
import re
from pathlib import Path
import sys
import yaml
ROOT = Path(__file__).resolve().parents[1]
DOMAINS_YAML = ROOT / "domains.yaml"
OUTPUT_DIR = ROOT / "bpm"
OUTPUT_FILE = OUTPUT_DIR / "Life-NOC.conf"
HOST_NAME = "life-noc"
TITLE = "Life-NOC"
OWNER = "icingadmin"
def slugify(value: str) -> str:
value = value.strip().lower()
replacements = {
"à": "a", "â": "a", "ä": "a",
"ç": "c",
"é": "e", "è": "e", "ê": "e", "ë": "e",
"î": "i", "ï": "i",
"ô": "o", "ö": "o",
"ù": "u", "û": "u", "ü": "u",
"ÿ": "y",
"œ": "oe", "æ": "ae",
"'": "", "": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^a-z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def aliasify(value: str) -> str:
value = value.strip().upper()
replacements = {
"À": "A", "Â": "A", "Ä": "A",
"Ç": "C",
"É": "E", "È": "E", "Ê": "E", "Ë": "E",
"Î": "I", "Ï": "I",
"Ô": "O", "Ö": "O",
"Ù": "U", "Û": "U", "Ü": "U",
"Ÿ": "Y",
"Œ": "OE", "Æ": "AE",
"'": "", "": "",
}
for old, new in replacements.items():
value = value.replace(old, new)
value = re.sub(r"[^A-Z0-9\-]+", "-", value)
value = re.sub(r"-{2,}", "-", value)
return value.strip("-")
def read_domains() -> dict:
data = yaml.safe_load(DOMAINS_YAML.read_text(encoding="utf-8"))
if not isinstance(data, dict) or "domains" not in data:
raise ValueError("domains.yaml doit contenir une clé racine 'domains'")
domains = data["domains"]
if not isinstance(domains, dict):
raise ValueError("domains.yaml: 'domains' doit être un mapping")
return domains
def collect_services(domain_data: dict, domain_name: str) -> list[str]:
items = domain_data.get("items", [])
if not isinstance(items, list):
raise ValueError(f"Domaine '{domain_name}': 'items' doit être une liste")
services: list[str] = []
for item in items:
if not isinstance(item, dict):
raise ValueError(f"Domaine '{domain_name}': item invalide (dict attendu)")
item_name = item.get("name")
if not item_name:
raise ValueError(f"Domaine '{domain_name}': item sans champ 'name'")
service_name = f"{slugify(domain_name)}-{slugify(str(item_name))}"
services.append(f"{HOST_NAME};{service_name}")
if not services:
raise ValueError(f"Domaine '{domain_name}' ne contient aucun item")
return services
def header() -> str:
return f"""### Business Process Config File ###
#
# Title : {TITLE}
# Description :
# Owner : {OWNER}
# AddToMenu : yes
# Backend :
# Statetype : soft
#
###################################
"""
def main() -> int:
domains = read_domains()
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
lines: list[str] = [header()]
aliases: list[tuple[str, str]] = []
for domain_name, domain_data in domains.items():
if not isinstance(domain_data, dict):
raise ValueError(f"Domaine '{domain_name}': structure invalide")
alias = aliasify(domain_name)
label = domain_data.get("label", domain_name)
services = collect_services(domain_data, domain_name)
expr = " & ".join(services)
lines.append(f"{alias} = {expr}\n")
aliases.append((alias, str(label)))
lines.append("\n")
root_expr = " & ".join(alias for alias, _label in aliases)
lines.append(f"LIFE-NOC = {root_expr}\n")
lines.append("display 1;LIFE-NOC;LIFE-NOC\n")
for alias, label in aliases:
lines.append(f"display 1;{alias};{label}\n")
OUTPUT_FILE.write_text("".join(lines), encoding="utf-8")
print(f"BPM généré: {OUTPUT_FILE.relative_to(ROOT)}")
print("Processus générés :")
print(" - LIFE-NOC")
for alias, _label in aliases:
print(f" - {alias}")
return 0
if __name__ == "__main__":
try:
raise SystemExit(main())
except Exception as exc:
print(f"ERREUR: {exc}", file=sys.stderr)
raise
PYEOF
chmod +x "$ROOT/scripts/generate_bpm.py"
echo "==> Ajustements texte ciblés"
python3 - "$ROOT" <<'PYEOF'
from pathlib import Path
import re
import sys
root = Path(sys.argv[1])
def replace_text(path_str, replacements):
path = root / path_str
if not path.exists():
return
text = path.read_text(encoding="utf-8")
original = text
for old, new in replacements:
text = text.replace(old, new)
if text != original:
path.write_text(text, encoding="utf-8")
print(f"Modifié: {path_str}")
def regex_replace(path_str, pattern, repl, must_match=False):
path = root / path_str
if not path.exists():
return
text = path.read_text(encoding="utf-8")
new_text, count = re.subn(pattern, repl, text, flags=re.MULTILINE)
if must_match and count == 0:
raise SystemExit(f"Aucun match dans {path_str} pour: {pattern}")
if new_text != text:
path.write_text(new_text, encoding="utf-8")
print(f"Modifié: {path_str}")
# Makefile
replace_text("Makefile", [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
])
# defaults main.yml
replace_text("ansible/roles/life_noc/defaults/main.yml", [
("/etc/icingaweb2/modules/businessprocess/processes/life-noc.json",
"/etc/icingaweb2/modules/businessprocess/processes/Life-NOC.conf"),
])
# tasks main.yml
replace_text("ansible/roles/life_noc/tasks/main.yml", [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
("life-noc.json", "Life-NOC.conf"),
])
# docs
for doc in ["preuve-de-reproductibilite.md", "decisions-techniques.md"]:
replace_text(doc, [
("bpm/life-noc.json", "bpm/Life-NOC.conf"),
("life-noc.json", "Life-NOC.conf"),
("export JSON", "export natif Business Process"),
("artefact JSON", "artefact natif Business Process"),
])
PYEOF
echo "==> Nettoyage de l'ancien artefact si présent"
rm -f "$ROOT/bpm/life-noc.json"
echo "==> Génération du nouveau BPM"
(
cd "$ROOT"
python3 scripts/generate_bpm.py
)
echo "==> Vérification rapide"
if [[ -f "$ROOT/bpm/Life-NOC.conf" ]]; then
echo "OK: bpm/Life-NOC.conf généré"
else
echo "ERREUR: bpm/Life-NOC.conf absent" >&2
exit 1
fi
echo
echo "Patch terminé."
echo "Fichiers sauvegardés dans: $ROOT/.patch-backup"
echo "Prochaine étape recommandée:"
echo " make check"
echo "Puis redéploiement avec BPM activé."