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