Cosa appartiene davvero al modulo ui.apps (e cosa no)
Una guida pratica a ciò che il modulo Maven ui.apps di un progetto AEM dovrebbe contenere — componenti, clientlib, dizionari i18n — e perché il contenuto redatto dagli autori non deve mai starci, con le regole del filter.xml di FileVault che decidono cosa viene sovrascritto o eliminato a ogni deploy.
ui.apps è il modulo Maven di un progetto AEM che costruisce un
content package FileVault che si installa in /apps. Questo lo sa
qualsiasi sviluppatore AEM. Ciò che causa incidenti reali è meno ovvio:
quali nodi esattamente possono vivere lì, e cosa fa il filter.xml del
package con tutto il resto che trova sotto quei percorsi al momento del
deploy. Se lo scope è definito male, un deploy di routine di ui.apps
può cancellare silenziosamente contenuto che non ha nulla a che fare con
la tua modifica.
Questo articolo presuppone che tu conosca già la struttura multimodulo
standard (core, ui.apps, ui.content, ui.frontend…) — si
concentra solo su cosa deve contenere ui.apps e su come il suo filtro
FileVault controlli cosa succede all’installazione.
Cosa consegna davvero ui.apps
ui.apps è un content package con packageType=application. Su AEM as
a Cloud Service, un package di tipo application può toccare solo
/apps — mai /content, /conf o qualsiasi altra area modificabile a
runtime. Ciò che appartiene sotto /apps è, per definizione, codice e
configurazione scritti dagli sviluppatori e distribuiti via CI/CD, non
contenuto redatto dagli autori nell’interfaccia di AEM:
- I componenti — la struttura di nodi
cq:Component: il.content.xml(dialog,sling:resourceType,sling:resourceSuperType), lo script HTL e qualsiasi registrazione di Sling Model che vive accanto sotto/apps/mysite/components/.... - Le definizioni delle cartelle clientlib — il nodo
cq:ClientLibraryFolderstesso:categories,embed,dependencies,jsProcessor/cssProcessor, e i manifestjs.txt/css.txtsotto/apps/mysite/clientlibs/.... Questo è distinto dal vero e proprio output JS/CSS minificato: quello di solito viene generato dal moduloui.frontendseparato e copiato in questa stessa struttura di cartelle durante la build Maven, ma la definizione della cartella — il nome della categoria, il grafo delle dipendenze — è contenuto diui.apps, non diui.frontend. - I dizionari i18n sotto
/apps— nodisling:MessageEntry/mix:Languageper le stringhe di UI di proprietà dello sviluppatore (etichette dei campi di dialog, testi di componente che non sono contenuto editabile) sotto/apps/mysite/i18n/....
Una cosa che sorprende i team abituati a pensare tutto in termini di
/apps: le definizioni di template editabili e le loro policy non
vengono distribuite da ui.apps, anche se sembrano “codice”. Un
cq:Template e i suoi nodi cq:Policy vivono sotto
/conf/mysite/settings/wcm/..., e /conf è un percorso mutabile,
modificabile dagli autori — le policy in particolare vengono modificate
regolarmente dagli autori tramite l’interfaccia “Edit Template” in
produzione. Poiché un package application può toccare solo /apps,
template e policy devono essere distribuiti da un package di tipo
content (di solito ui.content, o un modulo dedicato di
configurazione/struttura), tipicamente con mode="merge" in modo che un
redeploy non calpesti le modifiche di policy fatte da un autore dopo il
go-live. Ciò che appartiene davvero a ui.apps nel mondo dei template è
il codice del componente di struttura — l’HTL/Java dietro il layout
container — non il nodo del template né quello della policy.
Perché il contenuto redatto non deve mai vivere in ui.apps
Le pagine sotto /content, gli asset del DAM sotto /content/dam e i
tag usati per classificare quel contenuto sono compito di ui.content,
mai di ui.apps — e non è solo una questione di stile, è imposto da AEM
as a Cloud Service: un unico content package non può distribuire allo
stesso tempo su /apps e su un’area modificabile a runtime come
/content. Ma anche a prescindere da questa regola rigida, mescolare i
due causa danni reali:
- Le promozioni in Cloud Manager diventano più difficili da
ragionare. Le build full-stack impacchettano
ui.appsinsieme a tutto il resto; se un revisore non può dare per scontato che “questo package è puro codice”, ogni promozione richiede di ricontrollare se si è infiltrato del contenuto. - Il contenuto viene sovrascritto o eliminato silenziosamente al
deploy successivo.
ui.appsviene trattato come completamente sostituibile — ogni esecuzione della pipeline lo reinstalla da zero. La modalità di import predefinita di FileVault èreplace: tutto ciò che è coperto dal filtro del package ma non presente nell’archivio del package viene rimosso dal repository all’import. Una pagina o un asset finiti sotto un filter root diui.appshanno un’aspettativa di vita molto breve. - Il nodo sbagliato nel package sbagliato distrugge il lavoro di un
autore. Di solito succede per errore: qualcuno esegue
vlt checkouto esporta un package con un root troppo ampio, trascina dentro un nodo che un autore ha creato sotto un percorso che il package ora rivendica, e il deploy successivo diui.appslo elimina senza che nessuno abbia toccato direttamente Package Manager.
filter.xml: cosa controlla davvero un filter root
Ogni content package — ui.apps incluso — dichiara il proprio ambito in
META-INF/vault/filter.xml tramite elementi <filter root="...">. Un
filter root non è un suggerimento su “dove questo package mette di solito
le cose”; secondo la
documentazione di Apache Jackrabbit FileVault,
definisce il sottoalbero che il package possiede ai fini
dell’importazione:
- La modalità di import predefinita è
replace: il contenuto esistente sotto un root coperto viene sostituito da ciò che porta il package — sovrascritto o eliminato secondo necessità per corrispondere esattamente al package. - Un dettaglio cruciale: “i nodi/proprietà coperti da una regola di
filtro ma non contenuti nel contenuto da importare vengono rimossi dal
repository”. Se il tuo filter root è
/apps/mysitema iljcr_rootdel tuo package non contiene effettivamente un nodo che esiste già a destinazione sotto quel percorso, FileVault lo elimina all’installazione — è il filter root, non il contenuto del package, a decidere cosa rientra nell’ambito della cancellazione. - Il contenuto al di fuori di qualsiasi filter root dichiarato viene lasciato intatto, qualunque esso sia.
- Gli elementi
<include>/<exclude>all’interno di un<filter>restringono ulteriormente quel root. Vengono valutati in ordine contro il percorso JCR completo, e vince l’ultima regola che corrisponde — quindi ordinarli male cambia silenziosamente ciò che un filtro copre realmente.
Questo singolo fatto — i percorsi dichiarati ma non coperti dal contenuto del package vengono eliminati, non ignorati — è il meccanismo dietro quasi ogni incidente del tipo “un deploy ha cancellato contenuto che non c’entrava nulla con la mia modifica”.
Un filter.xml realistico per ui.apps
<?xml version="1.0" encoding="UTF-8"?>
<workspaceFilter version="1.0">
<filter root="/apps/mysite/components"/>
<filter root="/apps/mysite/clientlibs"/>
<filter root="/apps/mysite/i18n"/>
</workspaceFilter>
Tre root stretti ed espliciti — ognuno corrisponde esattamente al
sottoalbero di cui questo modulo è responsabile. Niente qui rivendica
/apps in blocco, e niente qui arriva a /conf o /content.
L’errore del root troppo ampio
<filter root="/apps"/>
Sembra una scorciatoia innocua — “possediamo /apps/mysite, e
/apps/mysite sta sotto /apps, quindi perché no”. Ma il filter root è
/apps stesso. All’installazione, FileVault considera ora che tutto
l’albero /apps — inclusi gli stessi Core Components di AEM sotto
/apps/core, il package di un altro team sotto /apps/othersite,
qualsiasi cosa — sia coperto da questo package. Poiché il jcr_root del
tuo package contiene realmente solo apps/mysite/..., tutto il resto
sotto /apps è “coperto ma non contenuto”, e viene eliminato
all’import. È esattamente così che un deploy di routine di ui.apps
manda giù i Core Components o un’applicazione gemella su un’istanza AEM
condivisa.
L’errore del root troppo stretto
<filter root="/apps/mysite/components"/>
<!-- qualcuno aggiunge /apps/mysite/templates/structure localmente
e dimentica di aggiungere un filter root per quel percorso -->
Il content-package-maven-plugin costruisce il package strettamente in
base a ciò che copre filter.xml. Una cartella aggiunta sotto jcr_root
che non è coperta da alcun filter root dichiarato viene semplicemente
esclusa dal package costruito — senza avviso, senza fallimento della
build. È committata in git, esiste su disco, e non arriva mai
all’istanza di destinazione. Questa è la modalità di fallimento più
silenziosa: nulla si rompe visibilmente, una funzionalità semplicemente
non compare mai, e la correzione di solito è “qualcuno ha dimenticato di
aggiungere una riga <filter root>”.
Dove emerge questo problema nella pratica
- Fallimenti di deploy in Cloud Manager dovuti a filter root
sovrapposti. La documentazione stessa di Adobe è esplicita nel dire
che il filtro di un container package non dovrebbe mai sovrapporsi al
filtro di un application package, e la stessa regola vale tra due
application package. Se
ui.appse un package di struttura del repository o di un vendor dichiarano entrambi un root che copre lo stesso percorso, la pipeline può fallire in fase di validazione o, peggio, installarsi con successo ma lasciare i due package a contendersi gli stessi nodi a ogni deploy successivo. Quando una pipeline di Cloud Manager fallisce nello step di deploy con errori legati ai package, confrontare ilfilter.xmldi tutti i content package della consegna tra loro è una delle prime cose da controllare. - Contenuto che scompare subito dopo un deploy di ui.apps. Se
qualcosa sotto
/appsscompare nel momento stesso in cui si installa un packageui.apps, il filter root è quasi sempre la risposta: o il root è più ampio del previsto e ha trascinato dentro qualcosa che non dovrebbe possedere, oppure un nodo prima coperto da un filtro più vecchio è rimasto orfano quando il filtro è stato ristretto senza prima migrare il contenuto. Confrontare ilfilter.xmltra la versione precedente e quella attuale del package — non solo il diff deljcr_root— è il modo più veloce per trovarlo.