260 lines
7.7 KiB
Bash
Executable file
260 lines
7.7 KiB
Bash
Executable file
#!/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é."
|