feat(app-dev): add dry-run preview to dev sync (#21251)

Split out of #21240. Stacked on #21250 (review/merge that first).

`yarn twenty dev --once --dry-run` computes the migration plan and
prints the diff **without applying anything** (no migration, no
app-record update, no SDK generation). Also renders the diff on a normal
`dev --once` sync.

<img width="646" height="179" alt="image"
src="https://github.com/user-attachments/assets/59f3ddcd-2a5b-4b8a-b21a-c659abe16af0"
/>
This commit is contained in:
martmull
2026-06-05 19:49:02 +02:00
committed by GitHub
parent bfb83e93b2
commit 6c65d26ced
19 changed files with 257 additions and 51 deletions
@@ -73,13 +73,16 @@ export class ApiService {
return this.applicationApi.createDevelopmentApplication(...args);
}
syncApplication(manifest: Manifest): Promise<
syncApplication(
manifest: Manifest,
options?: { dryRun?: boolean },
): Promise<
ApiResponse<{
applicationUniversalIdentifier: string;
actions: SyncAction[];
}>
> {
return this.applicationApi.syncApplication(manifest);
return this.applicationApi.syncApplication(manifest, options);
}
uninstallApplication(universalIdentifier: string): Promise<ApiResponse> {
@@ -252,7 +252,10 @@ export class ApplicationApi {
}
}
async syncApplication(manifest: Manifest): Promise<
async syncApplication(
manifest: Manifest,
options?: { dryRun?: boolean },
): Promise<
ApiResponse<{
applicationUniversalIdentifier: string;
actions: SyncAction[];
@@ -260,15 +263,15 @@ export class ApplicationApi {
> {
try {
const mutation = `
mutation SyncApplication($manifest: JSON!) {
syncApplication(manifest: $manifest) {
mutation SyncApplication($manifest: JSON!, $dryRun: Boolean) {
syncApplication(manifest: $manifest, dryRun: $dryRun) {
applicationUniversalIdentifier
actions
}
}
`;
const variables = { manifest };
const variables = { manifest, dryRun: options?.dryRun ?? false };
const response: AxiosResponse = await this.client.post(
'/metadata',