From a2ea98801f0e61e7834018f019eef5614add5ade Mon Sep 17 00:00:00 2001 From: Nicolas Cantu Date: Mon, 13 Apr 2026 23:50:55 +0200 Subject: [PATCH] feat(deploy): export SITE_CODE from conf; ensure nested secrets symlinks - deploy-conf-handling: default_site_code -> SITE_* when unset; symlink notary|enso|genealogie/ to ../ when flat / exists. - orchestrator and deploy-by-script-to: run before handoff to LeCoffre. - lecoffreio docs/Deployment.md: document behavior (-f: path was gitignored). --- deploy/deploy-by-script-to.sh | 7 +++ deploy/lib/deploy-conf-handling.sh | 55 +++++++++++++++++ deploy/orchestrator.sh | 2 + projects/lecoffreio/docs/Deployment.md | 81 ++++++++++++++++++++++++++ 4 files changed, 145 insertions(+) create mode 100644 projects/lecoffreio/docs/Deployment.md diff --git a/deploy/deploy-by-script-to.sh b/deploy/deploy-by-script-to.sh index 7e17d0e..5d5ddb8 100755 --- a/deploy/deploy-by-script-to.sh +++ b/deploy/deploy-by-script-to.sh @@ -90,6 +90,13 @@ else echo "[deploy-by-script-to] Step 2/5: secrets/${TARGET_BRANCH} OK (${SECRETS_DIR})" fi +# shellcheck source=lib/deploy-conf-handling.sh +source "${DEPLOY_IA}/lib/deploy-conf-handling.sh" +if [[ -n "${PROJECT_CONFIG_PATH:-}" && -f "${PROJECT_CONFIG_PATH}" ]]; then + ia_dev_deploy_export_site_code_from_conf "$PROJECT_CONFIG_PATH" + ia_dev_deploy_ensure_nested_secrets_symlinks "$SECRETS_PARENT" "$TARGET_BRANCH" +fi + if [[ "$HOST_STAYS_ON_TEST" != "true" ]]; then echo "[deploy-by-script-to] Step 3/5: force sync local branch with origin/${TARGET_BRANCH}..." git fetch origin diff --git a/deploy/lib/deploy-conf-handling.sh b/deploy/lib/deploy-conf-handling.sh index 5b826bb..a8bdac4 100644 --- a/deploy/lib/deploy-conf-handling.sh +++ b/deploy/lib/deploy-conf-handling.sh @@ -28,6 +28,61 @@ ia_dev_deploy_secrets_export_from_conf() { fi } +# ia_dev_deploy_export_site_code_from_conf — if SITE_CODE, LECOFFRE_SITE_CODE and DEPLOY_SITE_CODE are all unset, +# export them from deploy.default_site_code (LeCoffre multi-site: notary | enso | genealogie). Explicit env always wins. +ia_dev_deploy_export_site_code_from_conf() { + local conf="${1:?}" + if ! command -v jq >/dev/null 2>&1; then + return 0 + fi + if [[ -n "${SITE_CODE:-}" || -n "${LECOFFRE_SITE_CODE:-}" || -n "${DEPLOY_SITE_CODE:-}" ]]; then + return 0 + fi + local site + site="$(jq -r '.deploy.default_site_code // empty' "$conf" 2>/dev/null | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')" + if [[ -z "$site" || "$site" == "null" ]]; then + return 0 + fi + export SITE_CODE="$site" + export LECOFFRE_SITE_CODE="$site" + export DEPLOY_SITE_CODE="$site" + echo "[ia_dev][deploy] SITE_CODE unset; using deploy.default_site_code from conf: ${site}" >&2 +} + +# ia_dev_deploy_ensure_nested_secrets_symlinks +# LeCoffre connect-db-paths expects `.secrets///` under secrets_parent. ia_dev conf often has a flat `/` tree only. +# For each site in notary, enso, genealogie: if `//` is missing and `/` is a directory, +# create `/` as a symlink to `../` (same convention as a manual `ln -sfn ../pprod notary/pprod`). +# Skips a site when a real (non-symlink) directory already exists at the nested path. +ia_dev_deploy_ensure_nested_secrets_symlinks() { + local secrets_parent="${1:?}" + local env_name="${2:?}" + if [[ ! -d "$secrets_parent" ]]; then + return 0 + fi + if [[ ! "$env_name" =~ ^(test|pprod|prod)$ ]]; then + return 0 + fi + local flat="${secrets_parent}/${env_name}" + if [[ ! -d "$flat" ]]; then + return 0 + fi + local site nested + for site in notary enso genealogie; do + nested="${secrets_parent}/${site}/${env_name}" + if [[ -d "$nested" && ! -L "$nested" ]]; then + continue + fi + if [[ -e "$nested" && ! -L "$nested" ]]; then + echo "[ia_dev][deploy][WARN] ${nested} exists and is not a symlink or directory; skip" >&2 + continue + fi + mkdir -p "${secrets_parent}/${site}" + ln -sfn "../${env_name}" "$nested" + echo "[ia_dev][deploy] Nested secrets path: ${nested} -> ../${env_name}" >&2 + done +} + # ia_dev_deploy_export_runtime_context — required handoff to project orchestrator (blocking checks below) ia_dev_deploy_export_runtime_context() { local repo="${1:?}" diff --git a/deploy/orchestrator.sh b/deploy/orchestrator.sh index 244d383..6ac0ce3 100755 --- a/deploy/orchestrator.sh +++ b/deploy/orchestrator.sh @@ -48,6 +48,8 @@ fi ia_dev_deploy_require_jq "${_ORCH_TAG}" ia_dev_deploy_secrets_export_from_conf "$CONF" +ia_dev_deploy_export_site_code_from_conf "$CONF" +ia_dev_deploy_ensure_nested_secrets_symlinks "${SECRETS_BASE:-}" "${1:-}" ia_dev_deploy_export_runtime_context "$REPO_ROOT" "${1:-}" ia_dev_deploy_assert_handoff_context "$REPO_ROOT" "${1:-}" "${_ORCH_TAG}" diff --git a/projects/lecoffreio/docs/Deployment.md b/projects/lecoffreio/docs/Deployment.md new file mode 100644 index 0000000..96a5f9c --- /dev/null +++ b/projects/lecoffreio/docs/Deployment.md @@ -0,0 +1,81 @@ +# Deployment – Env, seeds, site_texts, vérifications + +**Auteur** : Équipe 4NK + +## Vérification site_texts (seeds, env, boot) + +Objectif : aligner les clés site_texts entre seeds, env et boot (test, pprod, prod). + +### 1. Seeds ↔ env (SITE_TEXTS_KEYS_INDEX) + +Les clés de `.secrets//seed-site-texts-.ts` (champ `textKey`) doivent être exactement celles listées dans `SITE_TEXTS_KEYS_INDEX` du fichier `.secrets//env-full--for-bdd-injection.txt`. + +**Commandes** : + +```bash +grep -oP 'textKey: "\K[^"]+' .secrets/test/seed-site-texts-test.ts | sort -u > /tmp/seed-keys.txt +grep '^SITE_TEXTS_KEYS_INDEX=' .secrets/test/env-full-test-for-bdd-injection.txt | sed 's/SITE_TEXTS_KEYS_INDEX=//' | tr ',' '\n' | sort -u > /tmp/env-keys.txt +diff /tmp/seed-keys.txt /tmp/env-keys.txt # doit être vide +``` + +### 2. Alignement test / pprod / prod + +Mêmes clés site_texts sur les trois environnements ; seuls les contenus peuvent différer (ex. mention [ENVIRONNEMENT DE TEST]). Comparer les listes de clés extraites des seeds test, pprod et prod. + +### 3. Boot : SITE_TEXTS_BOOT_SCOPES + +`SITE_TEXTS_BOOT_SCOPES` (dans `lecoffre-front-main/src/front/Stores/siteTextsBootConfig.ts`) doit couvrir tous les scopes chargés au boot. Valeur type : `["login", "my-account", "components", "folder", "forms", "documentTypes", "office", "thirdParty"]`. Aucun scope des seeds ne doit être absent du boot. + +### 3.1 Scope des textes « tiers » (`thirdParty.roleLabel.*`) + +- **Cible** : pour les clés dont le `textKey` commence par `thirdParty.` (ex. `thirdParty.roleLabel.courtier`), le champ **`scope`** en base et dans `seed-site-texts-.ts` doit être exactement **`thirdParty`** (camelCase), comme dans `defaultSiteTextFallbacks.ts`. Le préfixe API publique utilise ce même identifiant : `GET .../site-texts?scope=thirdParty,...`. +- **À ne pas confondre** : les clés **`forms.thirdParty.*`** (formulaire édition tiers) restent en scope **`forms`**. La variante UI `THIRD_PARTY_UI_VARIANT = "third-party"` (tiret) concerne le parcours / l’affichage membre, **pas** le scope `site_texts`. +- **Vérification en lecture seule** (sur la base V2 de l’environnement) : le script **ne se connecte pas à une base locale** ; il copie le SQL et un runner sur le **serveur de déploiement** (SSH, même mécanisme que `scripts/e2e-verify-db-env.sh`), puis exécute `psql` **sur la cible** en utilisant `.secrets//.env..connectDB` **présent sur ce serveur** (`DATABASE_HOST=db` → `127.0.0.1` côté cible). + +```bash +./scripts/verify-site-texts-thirdparty-scope-readonly.sh +``` + +Fichiers : `deploy/scripts_v2/remote/run-verify-site-texts-thirdparty-on-target.sh` (cible), `deploy/scripts_v2/remote/sql/verify-site-texts-thirdparty-scope-readonly.sql`. Équivalent SQL : + +```sql +SELECT DISTINCT scope, text_key +FROM site_texts +WHERE text_key LIKE 'thirdParty.%' +ORDER BY scope, text_key; +``` + +**Convention cible** : une seule valeur de `scope` pour ces lignes : **`thirdParty`** (cohérent avec le préfixe `thirdParty.` du `text_key`). + +**Données historiques** : d’anciennes lignes peuvent rester publiées avec **`scope: folder`** pour ces clés. Le backend **`getPublishedTextMap`** retient la version **publiée la plus récente** par `text_key` + locale (indépendamment du scope), afin qu’une migration seed vers **`thirdParty`** soit effective côté API. **Recommandation** : seeds sous **`thirdParty`** (voir `docs/fixKnowledge/site-texts-thirdparty-scope-seed-and-public-merge.md`) ; archiver les vieilles versions `folder` en super-admin si besoin de nettoyage. + +Si une valeur inattendue apparaît (`third-party`, etc.), **corriger le seed** et republier. N’ajouter un scope supplémentaire à `SITE_TEXTS_BOOT_SCOPES` qu’en dernier recours si des données historiques ne peuvent pas être migrées tout de suite. + +### 4. Évolution + +- Nouvelle clé dans un seed : l’ajouter à `SITE_TEXTS_KEYS_INDEX` du même env et aligner test/pprod/prod. +- Nouveau scope en base : l’ajouter à `SITE_TEXTS_BOOT_SCOPES` si nécessaire au chargement initial. + +--- + +## Fichiers de textes (validation métier) + +Liste des fichiers contenant les textes affichés à l’utilisateur (libellés, messages d’erreur, placeholders) à transmettre au métier pour validation : + +- **COMMON_UI_I18N** : commonUiI18nPart1.ts, commonUiI18nPart2.ts (modales, formulaires, dossiers, documents, rôles, types d’actes, souscription, Corbeille, erreurs métier, confrères, admin site texts, collaborateurs, etc.). +- **Upload / validation** : fileUploadUiI18n.ts, fileValidationI18n.ts. +- **Erreurs / login** : Utils/errorMessages/* (getUserFriendlyError, httpStatusMessages, humanizeError), loginErrorCodes.ts. +- **Site texts** : `.secrets//seed-site-texts-.ts` ; clés dans env-full-*-for-bdd-injection.txt (SITE_TEXTS_KEYS_INDEX). + +--- + +## Déploiement applicatif + +- **Multi-site (`SITE_CODE`, secrets imbriqués)** : les scripts applicatifs (`deploy/scripts_v2`, `connect-db-paths.sh`) attendent **`.secrets///`** avec **`site ∈ { notary, enso, genealogie }`**. Sous **`projects/lecoffreio/conf.json`**, **`deploy.secrets_path`** pointe souvent vers un arbre **plat** **`//`** uniquement. L’**orchestrateur ia_dev** (`deploy/orchestrator.sh`) et **`deploy/deploy-by-script-to.sh`** : (1) exportent **`SITE_CODE`**, **`LECOFFRE_SITE_CODE`** et **`DEPLOY_SITE_CODE`** à partir de **`deploy.default_site_code`** lorsque ces trois variables sont encore vides (une exportation explicite dans le shell reste prioritaire) ; (2) pour chaque site **notary**, **enso**, **genealogie**, si **`//`** n’existe pas encore et que **`//`** est un répertoire, créent un lien symbolique **`../`** (équivalent manuel : `mkdir -p notary && ln -sfn ../test notary/test`). Si un répertoire **réel** (non lien) existe déjà pour un site, il n’est pas écrasé. Pour des secrets **distincts** par site sur la même machine, remplacer le lien par un vrai répertoire et fichiers dédiés. +- **Orchestration ia_dev** : depuis la racine du dépôt ia_dev, `./deploy/deploy.sh [options]` exporte `IA_PROJECT_ID` puis exécute `orchestrator.sh`. Celui-ci enchaîne les scripts listés dans `deploy.hooks.phases` du `conf.json` du projet (chemins relatifs à `repository_root`), ou exécute `deploy.deploy_script_path` si `phases` est vide. `run-project-hooks.sh` délègue à `orchestrator.sh` (compatibilité). Cadrage : `deploy/DEPLOY_ORCHESTRATION_IA_DEV.md`. +- **Scripts** : `deploy/scripts_v2/` ; chemin projet dans ia_dev `projects/lecoffreio/conf.json` (project_path, build_dirs, deploy.deploy_script_path, deploy.secrets_path). +- **Clé SSH déploiement (`DEPLOY_SSH_KEY`)** : définie dans `.secrets//.env.`. Utiliser un chemin **portable** : par ex. `DEPLOY_SSH_KEY='$HOME/.ssh/id_ed25519_4nk'` (l’expansion de `$HOME`, `${HOME}` et un préfixe `~/` est appliquée par `lecoffre_deploy_resolve_deploy_ssh_key` après `load_dotenv_file_strict`, qui ne fait pas d’`eval`). Ne pas figer un autre compte système (`/home/desk/...`). Ordre de repli si la clé indiquée est absente : `$HOME/.ssh/id_ed25519_4nk`, puis `$HOME/.ssh/id_ed25519`. Avant toute connexion SSH, la clé retenue est validée avec `ssh-keygen -y`. Vérification réseau : `bash deploy/scripts_v2/run-verify-ssh.sh ` ou les trois d’un coup : `bash deploy/scripts_v2/run-verify-ssh-all-envs.sh`. +- **Modifications locales** : en **test**, le script `deploy.sh` met en stash les changements non commités avant pull/require_clean et les remet en place (stash pop) en fin de déploiement réussi. En **pprod** et **prod**, les changements locaux sont mis en stash sans remise en place (pas de développement local à conserver). +- **Versions** : version.package_json_paths (backend, frontend) ; splash_app_name pour la notice. +- **Secrets** : `.secrets//env-full--for-bdd-injection.txt` pour l’injection en BDD ; NOTARY_AI_AGENT_URL, NOTARY_AI_AGENT_TOKEN pour l’agent IA notaire (voir API.md). +- **Cartographie des checks de déploiement** : référence unique dans la doc projet (ex. projects/lecoffreio/docs ou doc dédiée) ; ne pas dupliquer ici.