Build, base de donnees, uSync, deploiement
Spécificité intranet PEL (15.07.2026) : l'hébergement de production sera Windows IIS chez Pully, pas le pipeline Docker Linux décrit ci-dessous (qui reste valable pour un éventuel staging Spektrum). Déploiement IIS :
dotnet publish+ ASP.NET Core Module (modalités exactes — WebDeploy/zip, pool, in-process — à préciser au compte-rendu du SSI). Conséquence fonctionnelle : le SSO Windows Authentication est possible (voirdomain/business-rules.md, section Intranet & AD).
Build
- Backend :
dotnet build Sentinelle.sln(.NET 10) construit les 5 projets de la solution :Web(executable),Intranet.Core,Intranet.Connectivity(bibliotheques referencees parWeb, voiroverview.md),Tests.Common,Tests.Web.dotnet run --project src/Web/Web.csprojpour lancer en local - seulWebest executable. - Frontend : depuis
src/Web,npm ci+npm run build:prod(lint + Vite prod). Voirfrontend-build.md. Web.csproj:CopyRazorGenerateFilesToPublishDirectory=true(vues backoffice),rewriteRules.xmlcopie en sortie/publish (PreserveNewest), ICU app-local (Microsoft.ICU.ICU4C.Runtime),ProjectReferenceversIntranet.CoreetIntranet.Connectivity.dotnet publishsurWeb.csprojpublie transitivement les deux.
Base de donnees
- SQL Server. Chaine par defaut (
appsettings.json) :Server=localhost\sqlexpress;Database=UmbracoBaseTemplate;User Id=dev;Password=dev;TrustServerCertificate=true;, providerMicrosoft.Data.SqlClient. - Override par
appsettings.local.json(git-ignore) ou variable d'env. Umbraco:CMS:Unattended:UpgradeUnattended=true: Umbraco applique les migrations au boot (pas de restauration manuelle ni d'etape d'install).
uSync
uSync 17.3.5. Config serialisee soussrc/Web/uSync/v17/(ContentTypes, DataTypes, Dictionary, Languages, MediaTypes, MemberTypes, Templates).- Reglages (
appsettings.json) :ExportOnSave="Settings",ImportAtStartup="Settings",UIEnabledGroups="Settings". Le handler Dictionnaire est enCreateOnly(les traductions ne sont pas ecrasees a l'import). - Workflow : modifier dans le backoffice -> uSync exporte les
.config; au demarrage, uSync importe le groupeSettings.
Docker
hosting/Dockerfile (build multi-stage, build depuis la racine du repo, pas depuis hosting/) — 3 stages :
frontend(node:22.12.0-slim) :npm cipuisnpm run build:prod(lint ESLint + Vite prod). Copiescripts/,styles/etViews/(les vues.cshtmlsont le contenu PurgeCSS ; sans elles PurgeCSS purge tout le CSS), plusvite.config*.jseteslint.config.js. Sortie :wwwroot/css/<theme>/main.css+wwwroot/js/.build(dotnet/sdk:10.0) :COPY ./src/ ./(tout le dossiersrc/, et non plus seulementsrc/Web/- depuis le split en 3 projets,WebreferenceIntranet.Core/Intranet.ConnectivityparProjectReferencerelatif,dotnet restore/publisha besoin des.csprojdes trois), puisWORKDIR /src/Webavant d'ecraserwwwroot/css/wwwroot/jsavec la sortie du stagefrontend,dotnet restore+dotnet publish -c Release -o /app/publish(execute depuisWeb.csproj, publie transitivementIntranet.Core/Intranet.Connectivity).runtime(dotnet/sdk:10.0) : copie/app/publish,ENTRYPOINT ["dotnet", "Web.dll"].
Non verifie : ce changement de contexte de build (COPY ./src/ au lieu de COPY ./src/Web/, WORKDIR /src/Web ajoute avant dotnet restore) accompagne le split en 3 projets mais n'a pas ete valide par un docker build reel dans cette tache - a executer avant le premier deploiement qui suit le split.
Point de vigilance - pas de .dockerignore : le repo n'a aucun .dockerignore. COPY ./src/ ./ embarque donc aussi les bin//obj/ des trois projets (Web, Intranet.Core, Intranet.Connectivity) s'ils existent localement au moment du build, ce qui alourdit le contexte envoye au daemon Docker et peut, dans de rares cas, faire fuiter des artefacts de build locaux dans l'image intermediaire. Purger bin/obj avant un docker build local, ou ajouter un .dockerignore (**/bin/, **/obj/).
Build / push (registry interne registry.internal.spektrum-suisse.ch, sans authentification) :
docker build -t registry.internal.spektrum-suisse.ch/sentinelle-website:<tag> -f hosting/Dockerfile .
docker push registry.internal.spektrum-suisse.ch/sentinelle-website:<tag><tag> = staging (branche staging) ou prod (branche master). Le pipeline pousse aussi un tag :$CI_COMMIT_SHORT_SHA par commit.
Version affichee dans le dashboard "Système" : les ARG/ENV GIT_COMMIT_* du stage runtime alimentent la boite "Version & build" du backoffice (voir modules/backoffice.md). Pour renseigner la version au build, passer les infos git en --build-arg :
docker build \
--build-arg GIT_COMMIT_COUNT="$(git rev-list --count HEAD)" \
--build-arg GIT_COMMIT_SHA="$(git rev-parse --short HEAD)" \
--build-arg GIT_COMMIT_DATE="$(git log -1 --format=%cI)" \
-t columbia-registry.spektrum.media/umb_<site>:<env> -f hosting/Dockerfile .Sans ces --build-arg, la version tombe sur 1.0.0.dev (l'image runtime aspnet:10.0 ne contient pas git : aucun fallback CLI, la version vient uniquement des env vars). Le .gitlab-ci.yml du template ne construit pas d'image Docker (build/push manuels) : il n'y a donc pas de job CI ou passer ces build-args. Un projet aval qui ajoute un job docker en CI doit y injecter les --build-arg lui-meme.
hosting/docker-compose.yml (exemple a adapter, placeholders <mon-site>/<env>/<mon-port-externe>) : monte appsettings.json (ro), media/, logs/ en volumes, reseau externe app-network, ASPNETCORE_ENVIRONMENT=Staging, ASPNETCORE_URLS=http://*:5000, forwarded headers actives.
CI/CD (GitLab)
.gitlab-ci.yml. Trois stages : build, deploy, docs.
Validation (stage build, merge requests uniquement, rules: CI_PIPELINE_SOURCE == "merge_request_event") :
build:backend: imagedotnet/sdk:10.0,dotnet build Sentinelle.sln --configuration Release.build:frontend: imagenode:22.12.0,cd src/Web && npm ci && npm run build:prod.
Deploiement Docker (stage deploy, docker:25 + service docker:25-dind, tag runner linux) — construit et pousse l'image registry.internal.spektrum-suisse.ch/sentinelle-website (registry sans auth, pas de docker login). Template .deploy_template : docker pull du tag de cache, docker build --cache-from, puis push du tag primaire ET du $CI_COMMIT_SHORT_SHA.
deploy:staging: push sur la branchestaging-> tag:staging.deploy:production: push sur la branchemaster-> tag:prod.
workflow.rules autorise le pipeline pour : merge requests ciblant master, commits sur master, commits sur staging.
Un bloc docs (composant sync-claude-docs) synchronise .claude/docs/** vers le site de doc interne sur master/main/staging (gate sur changements de .claude/docs/**).
Sentry
Charge uniquement en Staging/Production et si SentryUrl est defini (appsettings.json, vide par defaut, a injecter sans le committer). Prod : TracesSampleRate=0.1. Staging : ProfilingIntegration a 500 ms et Debug=true.
URL rewrites
src/Web/rewriteRules.xml (format IIS) applique via app.UseRewriter() en Staging/Production uniquement.
Déploiement APOL - Entra ID (M365)
Variables propres à un déploiement APOL (skin Entra ID, voir modules/extensibility.md et domain/business-rules.md). Section absente = brique inerte (comme Ldap pour PEL) ; ne pas la configurer en local.
- Section
EntraId(appsettings.<Env>.jsonou variables d'env) :TenantId,ClientId,CallbackPath(garder la valeur par défaut/umbraco-entra-members-signinsauf besoin explicite - un changement doit être répercuté dans l'allowlist deMemberGateMiddleware). - Secret
ClientSecret: jamais committé, viaappsettings.local.json(git-ignore) ou variable d'envEntraId__ClientSecret, commeLdap__ServicePassword. - App Registration Entra : l'URI de redirection déclarée côté Azure/Entra doit correspondre exactement à
CallbackPath(https://<host-apol>/umbraco-entra-members-signin). Intranet:LoginPath=/account/microsoft(point d'entrée du challenge OAuth, remplace/loginutilisé côté PEL) etIntranet:RequireLogin=true.
Exemple de config :
"EntraId": {
"TenantId": "<tenant-guid-apol>",
"ClientId": "<client-id-app-registration>",
"CallbackPath": "/umbraco-entra-members-signin"
},
"Intranet": { "RequireLogin": true, "LoginPath": "/account/microsoft" }Breaking change Umbraco 17.4 - URL applicative
Depuis 17.4, la detection auto de l'URL via le header Host est optionnelle (risque de spoofing sur emails de reset/invitation). Le template fournit une section vide Umbraco:CMS:WebRouting:UmbracoApplicationUrl. Tout projet aval derriere un reverse proxy doit la renseigner (appsettings de prod ou variable d'env Umbraco__CMS__WebRouting__UmbracoApplicationUrl).

