Vai al contenuto principale

Panoramica

Lo sviluppo di app in locale ruota attorno alla sincronizzazione: la CLI ricostruisce il tuo manifest e il server applica solo la differenza tra questo e i metadati già presenti nel tuo spazio di lavoro. Questa pagina spiega quale comando usare, come leggere ciò che una sincronizzazione ha modificato e cosa fare — in ordine — quando lo stato locale sembra incoerente.

Quale comando, quando

Per l’iterazione locale quotidiana vuoi quasi sempre twenty app dev. Il deploy e la pubblicazione servono a distribuire le release, non per il ciclo locale.

La sincronizzazione locale non richiede un incremento di versione

La regola della version strettamente crescente (VERSION_ALREADY_EXISTS in fase di deploy, APP_ALREADY_INSTALLED / CANNOT_DOWNGRADE_APPLICATION in fase di installazione) si applica a twenty app publish / twenty app install — il percorso di release. twenty app dev sincronizza il tuo manifest in-place e non richiede mai una modifica di versione, quindi non devi toccare package.json per iterare. Se ti ritrovi ad aumentare la versione per testare una modifica locale, stai usando il percorso di release quando invece vuoi il ciclo di sviluppo.

Lettura dell’output di sincronizzazione

Ogni sincronizzazione stampa le modifiche ai metadati che ha applicato (o che applicherebbe, con plan), in stile Terraform — un blocco per entità con i relativi attributi, quindi una riga di riepilogo:
Questo è il tuo primo strumento diagnostico: ti dice esattamente quali oggetti, campi e layout sono cambiati, così puoi confermare che una sincronizzazione ha fatto ciò che ti aspettavi prima di controllare l’interfaccia utente (UI). Le modifiche distruttive (to destroy) sono elencate con ciò che eliminano (ad es. objectMetadata "auditNote" — drops the table and all its rows) e richiedono una conferma interattiva, oppure --yes negli script. Una sincronizzazione elimina ogni entità di proprietà dell’app che non è più dichiarata dalla sorgente. Quando un piano contiene operazioni di eliminazione, lo indica e rimanda a --no-prune (in precedenza --no-delete, che continua a funzionare), rendendo la sincronizzazione additiva: vengono applicate le creazioni e gli aggiornamenti, non viene eliminato nulla che manchi dalla sorgente e il componente delle impostazioni e le associazioni dell’hook di disinstallazione dell’app rimangono invariati quando la sorgente non li dichiara. Usalo quando la sorgente è intenzionalmente parziale, ad esempio durante l’adozione di un’app creata nell’interfaccia utente. L’applicazione personalizzata dell’area di lavoro è l’eccezione: poiché pull non può ancora esportare tutto ciò che possiede, la sua sincronizzazione è additiva a meno che non specifichi --prune. I server rilasciati prima di questa modifica eliminano ancora per impostazione predefinita, quindi specifica --no-prune quando applichi l’applicazione personalizzata a uno di essi. Quando una sincronizzazione fallisce su una singola entità, l’errore indica l’entità in questione e il suo universalIdentifier, per esempio:
Usa quell’identificatore per trovare l’entità nel tuo manifest (e, se necessario, nello spazio di lavoro) invece di indovinare quale sia in conflitto.

Anteprima delle modifiche (plan)

twenty app plan crea il tuo manifest, chiede al server il piano di migrazione e lo stampa — senza applicare nulla. È il modo sicuro per rispondere a “cosa cambierebbe questa sincronizzazione?” prima di impegnarti ad applicarla.
Un piano:
  • Non modifica il workspace, non esegue la migrazione dei metadati, l’aggiornamento dei record dell’applicazione o modifiche ai ruoli o alle schede predefiniti. Crea uno snapshot locale, ma non rigenera il client API.
  • Restituisce lo stesso diff che una sincronizzazione reale applicherebbe, così puoi esaminare in anticipo le entità create/aggiornate/eliminate.
  • È utile prima di una modifica rischiosa, quando si rivede una modifica generata da un’IA o in uno script che deve fallire se sta per essere applicata una modifica imprevista.
Un piano mostra in anteprima solo le modifiche ai metadati. Funziona anche per un’app che non è stata mai sincronizzata: il server valuta il manifest rispetto a un’applicazione vuota, quindi il piano elenca tutto ciò che il tuo codice sorgente creerebbe.

Riportare al codice sorgente un’app installata

twenty app pull è sperimentale. Al momento copre solo una parte di un’applicazione, la forma del suo output potrebbe cambiare tra le release e sovrascrive i file che definiscono le entità che estrae. Ripristina le traduzioni pubblicate in locales/, mantenendo le voci le cui stringhe di origine non sono ancora nel tuo albero in locales/compiled/, affinché un push non le perda. Esegui il commit del tuo lavoro prima di eseguirlo e controlla il diff.
twenty app pull è l’inverso di apply: legge un’applicazione dall’area di lavoro e ripristina i relativi file di definizione, le traduzioni e i file delle dipendenze archiviate.
All’interno di un’app, pull legge il proprio identificatore defineApplication() locale e recupera quell’applicazione. All’esterno di un’app, richiede una directory e una conferma, crea un progetto locale per l’applicazione personalizzata dell’area di lavoro selezionata, installa le dipendenze con Yarn, quindi recupera le definizioni. Il nuovo progetto include file di pacchetto e configurazione, senza le entità demo dell’inizializzatore. Usa --path per selezionare un’app esistente o una directory nuova o vuota. La creazione non interattiva richiede --path e --yes; pull si rifiuta di sovrascrivere una directory non vuota non correlata. La creazione di un progetto non registra un’applicazione né modifica l’area di lavoro. Un progetto SDK esistente senza una definizione dell’applicazione recupera anche l’applicazione personalizzata. Non esiste alcuna opzione --universal-identifier. Se l’installazione delle dipendenze non riesce per un nuovo progetto, lo scaffold viene mantenuto. Esegui yarn install al suo interno, quindi riprova il pull. I progetti esistenti non installano automaticamente le dipendenze. Se non è possibile leggere il relativo identificatore dell’applicazione, pull segnala quell’errore invece di creare un’altra app. Aggiungi --verbose per espandere le sezioni di copertura del report, che indicheranno fino a 20 identificatori per tipo di metadati anziché solo i totali. Cosa scrive:
  • la configurazione dell’applicazione;
  • file delle dipendenze package.json e yarn.lock archiviati, nella radice del progetto;
  • oggetti, con i relativi campi inclusi;
  • campi aggiunti dalla tua app a oggetti che non possiede;
  • indici creati;
  • flag di autorizzazione dichiarati dall’app, come file definePermissionFlag();
  • ruoli, con le relative autorizzazioni per gli oggetti, autorizzazioni per i campi, predicati di autorizzazione a livello di riga e gruppi, nonché flag di autorizzazione inline. Il ruolo predefinito dell’app viene scritto con defineApplicationRole(). Un’autorizzazione per un oggetto o un campo il cui identificatore corrisponde a quello derivato dalla build viene scritta senza di esso. Quando il ruolo predefinito non è uno dei ruoli dell’app, come per l’applicazione Custom dello spazio di lavoro, la configurazione dell’app mantiene invece defaultRoleUniversalIdentifier;
  • viste, con i relativi campi, gruppi di campi, filtri, gruppi di filtri, ordinamenti e gruppi in linea;
  • campi della vista aggiunti dalla tua app alle viste di cui non è proprietaria, come file defineViewField() autonomi;
  • layout di pagina, con le relative schede e widget in linea;
  • schede che la tua app ha aggiunto a layout di pagina di cui non è proprietaria, come file definePageLayoutTab() autonomi;
  • voci del menu di navigazione, inclusi file e cartelle defineNavigationMenuItem();
  • widget aggiunti dall’applicazione a schede di cui non è proprietaria, come file definePageLayoutWidget() autonomi;
  • workflow, come file defineWorkflow(), con il codice sorgente di ogni passaggio Code nel rispettivo file \<workflow-file-name>/\<step-name>.code.ts accanto al workflow, quando la versione archiviata è quella scritta dall’ultima sincronizzazione. Per l’applicazione Custom dell’area di lavoro, è la versione attiva, sia che l’abbia pubblicata una sincronizzazione o il builder di workflow, e l’applicazione pubblica una definizione modificata come nuova versione, come descritto in Workflow;
  • competenze, come file defineSkill();
  • agenti, come file defineAgent(), con i relativi trigger e il ruolo a cui la tua app li associa;
  • regole di convalida, come file defineValidationRule() con la relativa espressione come stringa;
  • traduzioni pubblicate, come locales/\<locale>.json affinché strings pull possa rileggerle dai metadati e dalla sorgente scaricati, e locales/compiled/\<locale>.json per il resto.
Ogni entità viene inserita nel file che già la definisce, quindi un secondo pull riscrive solo ciò che è cambiato sul server. Un file viene riscritto anche quando utilizza una funzione di definizione diversa da quella che pull scriverebbe, ad esempio un ruolo che è diventato o ha smesso di essere il ruolo predefinito. Le nuove entità vengono collocate accanto ai file esistenti dello stesso tipo e un nome generato che finirebbe in un file non correlato viene qualificato anziché sovrascriverlo. File di dipendenza. Quando il file package.json o yarn.lock archiviato cambia, pull sostituisce il file locale e segnala eventuali modifiche locali sovrascritte. Se il file remoto non è cambiato dall’ultimo pull, le modifiche locali vengono mantenute. L’assenza di un file remoto non elimina la sua controparte locale. Quando il pacchetto esportato omette twenty-sdk, come fa l’applicazione Custom dello spazio di lavoro, pull conserva la dipendenza SDK locale affinché i comandi CLI possano comunque caricare il progetto. I file di dipendenza, i metadati e la baseline vengono scritti e ripristinati insieme se una scrittura non riesce. Esegui yarn install dopo aver scaricato le modifiche alle dipendenze. Pull installa le dipendenze solo durante la creazione di un nuovo progetto, prima di ripristinare i file remoti; non le reinstalla dopo aver sostituito package.json o yarn.lock. Pull non rigenera il client tipizzato. Esegui twenty app generate-client quando ti servono tipi del workspace aggiornati. Cosa non scrive:
  • Non ancora esportati — voci del menu dei comandi e del menu delle impostazioni, funzioni logiche e componenti front-end, provider di connessione, variabili dell’applicazione, risorse pubbliche, tipi di attività della timeline e il codice sorgente archiviato delle tue funzioni.
  • Derivati dal motore — campi di sistema, relazioni predefinite, viste predefinite e indici sottostanti, e i layout di pagina predefiniti di ciascun oggetto. Il server li ricostruisce durante apply. Un oggetto può comunque puntare il proprio identificatore dell’etichetta a uno di essi, come fanno gli oggetti di giunzione creati nell’interfaccia utente; il puntatore viene scritto e la compilazione lo risolve senza aggiungere un campo.
  • Inseriti in una vista predefinita o esterna — i filtri, i gruppi di filtri, gli ordinamenti, i gruppi e i gruppi di campi aggiunti a una vista predefinita del motore o a una vista di proprietà di un’altra applicazione non dispongono ancora di un modulo di origine e vengono segnalati. I campi della vista aggiunti lì vengono scritti come file autonomi, poiché l’identificatore della vista è noto.
  • Inseriti in un layout di pagina predefinito o esterno — le schede e i widget aggiunti a un layout di pagina predefinito del motore, o a uno di proprietà di un’altra applicazione, vengono scritti come file definePageLayoutTab() e definePageLayoutWidget() autonomi, poiché l’identificatore a cui si collegano è noto.
  • Widget che l’esportazione non può ancora includere — in qualsiasi layout, incluso il tuo, viene segnalato un widget del tipo deprecato VIEW, un widget che mostra uno dei componenti front o delle voci del menu dei comandi della tua app, oppure un widget senza una posizione per la modalità di layout della relativa scheda, e il layout o la scheda viene scritto senza di esso.
  • Workflow che l’esportazione non può ancora gestire — un workflow modificato nell’editor dei workflow dall’ultima sincronizzazione, un workflow di sistema o privato, un workflow con un passaggio Code creato prima che i passaggi memorizzassero il proprio codice sorgente e un workflow che richiama l’azione workflow di un’altra app vengono segnalati e lasciati invariati. Un workflow lasciato invariato mantiene i propri file .code.ts. L’applicazione Custom dell’area di lavoro è l’eccezione per il primo e l’ultimo caso: il builder pubblica anche i relativi workflow, che possono chiamare le azioni di qualsiasi app, quindi vengono recuperati. Uno dei relativi workflow viene invece segnalato quando non ha una versione attiva oppure quando la sua versione attiva utilizza impostazioni che una definizione non può esprimere. I passaggi email e calendario vengono acquisiti: un account condiviso con lo spazio di lavoro viene scritto come relativo connectedAccountHandle, mentre l’account privato di un membro e le variabili vengono mantenuti così come sono. Il workflow viene invece segnalato quando l’account di un passaggio è archiviato, non può più eseguire il passaggio o condivide il proprio indirizzo con un account meno recente, poiché l’applicazione dell’handle selezionerebbe un altro account.
  • Agenti che l’esportazione non può ancora gestire — un agente con più di un ruolo, o associato a un pull del ruolo su cui non scrive, viene segnalato e lasciato in posizione.
  • Regole di convalida che l’esportazione non può ancora trasferire — una regola senza espressione o messaggio, come una bozza non completata da Impostazioni o una regola che legge un campo eliminato dall’area di lavoro, viene segnalata e lasciata al suo posto.
  • Stato di runtime dello spazio di lavoro, allegati ai record, allegati email, assegnazioni di ruoli per membri e chiavi API, webhook, navigazione per utente ed elementi di navigazione fissati a un record, tutti esclusi per progettazione, inclusa l’applicazione Custom dello spazio di lavoro.
  • Di proprietà di un’altra applicazione — segnalati, mai estratti.
Il report alla fine di un pull elenca tutto, raggruppato per tipo e motivo. Tutto ciò che indica come non scritto è ancora nell’area di lavoro, quindi reinvia con push un albero ottenuto con pull usando twenty app apply --no-prune finché non saranno supportati i tipi mancanti. L’applicazione personalizzata dell’area di lavoro viene già applicata in questo modo per impostazione predefinita. Un apply con pruning (l’impostazione predefinita per le altre app, --prune per l’applicazione personalizzata) li eliminerebbe, e twenty app plan elenca esattamente quali. Stato locale dell’area di lavoro. Un’entità disattivata nell’area di lavoro viene scritta come definizione attiva e le modifiche apportate dall’area di lavoro a un’entità, ad esempio un’etichetta rinominata, non vengono scritte. Il report elenca tali righe in “Scritte senza il relativo stato locale dell’area di lavoro”. Riapplicare l’albero alla stessa area di lavoro lascia invariato tale stato; un’altra area di lavoro riceve l’entità attiva e senza le modifiche. Il file di base. Pull registra ciò che ha scaricato in .twenty/cli/pull-base.json, che è ignorato da git e specifico per ogni checkout. Quel file è ciò con cui un pull successivo confronta per non modificare i file le cui entità non sono cambiate e per eliminare il file di un’entità che non è più nell’area di lavoro. Un checkout che ne è privo scrive semplicemente tutto e non elimina nulla.

Scala di ripristino

Quando i metadati locali sembrano errati, procedi in quest’ordine e fermati non appena ti sblocchi. Ogni passaggio è più invasivo del precedente.
  1. Nuova sincronizzazione. Esegui di nuovo twenty app apply. Le sincronizzazioni sono idempotenti — rieseguire un manifest pulito è sicuro e spesso risolve un problema temporaneo.
  2. Visualizza in anteprima il piano. Esegui twenty app plan per vedere esattamente cosa intende cambiare la prossima sincronizzazione, senza applicarlo.
  3. Leggi l’errore nominale. Se una sincronizzazione fallisce, annota il tipo di metadato e lo universalIdentifier nel messaggio (vedi sopra) e individua quell’entità nel tuo manifest. Un conflitto di solito indica un identificatore duplicato o riutilizzato.
  4. Disinstalla e reinstalla. twenty app uninstall, poi sincronizza di nuovo (twenty app dev). Questo ricostruisce i metadati dell’app partendo da zero, lasciando intatto il resto del tuo spazio di lavoro.
  5. Reimpostazione completa (ultima risorsa). Per un ambiente locale gestito dalla CLI, esegui twenty local reset, quindi twenty local start e riconnettiti prima di sincronizzare nuovamente. Per un checkout del codice sorgente, reimposta e inizializza nuovamente con dati seed il relativo database di sviluppo.
La reimpostazione dell’ambiente locale o del database di sviluppo del repository elimina tutti i dati nella tua istanza locale, ogni workspace, record e app. Usalo solo quando i passaggi precedenti hanno fallito.
Hai riscontrato un errore di metadati? Per favore apri una issue e includi il messaggio di migrazione non riuscita (con il tipo di metadato e lo universalIdentifier), l’output Metadata changes della sincronizzazione e i comandi che hai eseguito.

Evitare sincronizzazioni concorrenti sullo stesso spazio di lavoro

La sincronizzazione applica le migrazioni dei metadati. Eseguire diverse operazioni di sincronizzazione, deploy o installazione sullo stesso spazio di lavoro nello stesso momento — per esempio, più terminali o agenti di IA che iterano in parallelo — può intrecciare queste migrazioni e lasciare i metadati in uno stato parzialmente applicato. Il server serializza le sincronizzazioni per spazio di lavoro per evitare questo, ma dovresti comunque convogliare le operazioni sensibili sui metadati attraverso un unico processo invece di eseguirle in parallelo. Se orchestri lo sviluppo con più agenti, instrada le loro chiamate di sync/deploy/install attraverso una coda unica in modo che ne venga eseguita solo una alla volta.

Distinguere i tipi di errore

Quando qualcosa va storto, il diff dei metadati e gli errori nominali ti permettono di localizzare il problema:
  • Errore di build del manifest — la CLI fallisce prima della sincronizzazione (MANIFEST_BUILD_FAILED, TYPECHECK_FAILED); correggi il sorgente della tua app.
  • Errore di titolarità della registrazione — la sincronizzazione viene rifiutata perché l’universalIdentifier dell’app appartiene a un altro spazio di lavoro o a nessuno spazio di lavoro; consulta Titolarità della registrazione.
  • Errore di sincronizzazione / migrazione — la build riesce ma l’applicazione del diff fallisce, indicando l’entità e lo universalIdentifier; correggi i metadati in conflitto.
  • Errore dimensione delle dipendenze — la sincronizzazione o l’installazione non riesce perché le dependencies di produzione dell’app sono troppo grandi per essere installate come layer di runtime (LOGIC_FUNCTION_DEPENDENCIES_SIZE_EXCEEDED); sposta nei devDependencies i pacchetti che le tue funzioni logiche non importano a runtime (librerie UI, strumenti di sviluppo).
  • Migrazione differita in corso — la sincronizzazione viene rifiutata perché è ancora in esecuzione una migrazione in background del modello di dati da una sincronizzazione precedente (DEFERRED_WORKSPACE_MIGRATION_ACTIONS_IN_PROGRESS); attendi che termini, quindi sincronizza di nuovo.
  • Errore di runtime del codice dell’app — la sincronizzazione va a buon fine ma le tue funzioni di logica o i componenti si comportano in modo anomalo in fase di esecuzione; controlla i log delle funzioni.
  • Stato locale dell’istanza — nessuno dei casi precedenti e lo spazio di lavoro continua a sembrare errato; procedi lungo la scala di ripristino.