Dirty fix twenty cli ci build order issue (#16024)

# Introduction
- fix twenty-apps hello world deps lockfile 
- improve error response format in application resolver
- fixed tests by adding applicationId back to serverless function
service v2

## New log format
We should have a tmp logs folder where we write the errors so the user
can see the whole of them such as what's done in yarn logs
```ts
    console.log
      ✓ Client generated successfully!

      at GenerateService.generateClient (src/services/generate.service.ts:58:13)

    console.log
      Generated files at: /Users/paulrastoin/ws/twenty/packages/twenty-apps/hello-world/generated

      at GenerateService.generateClient (src/services/generate.service.ts:59:13)

    console.error
       Serverless functions Sync failed: {
        message: 'Multiple validation errors occurred while creating serverless function',
        extensions: {
          code: 'METADATA_VALIDATION_FAILED',
          errors: {
            fieldMetadata: [],
            objectMetadata: [],
            view: [],
            viewField: [],
            viewGroup: [],
            index: [],
            serverlessFunction: [Array],
            cronTrigger: [],
            databaseEventTrigger: [],
            routeTrigger: [],
            viewFilter: []
          },
          summary: {
            invalidViewFilter: 0,
            invalidObjectMetadata: 0,
            invalidView: 0,
            invalidViewField: 0,
            invalidIndex: 0,
            invalidServerlessFunction: 0,
            invalidDatabaseEventTrigger: 0,
            invalidCronTrigger: 0,
            invalidRouteTrigger: 0,
            invalidFieldMetadata: 0,
            invalidViewGroup: 0,
            totalErrors: 0
          },
          message: 'Validation failed for 0 object(s) and 0 field(s)',
          userFriendlyMessage: 'Validation failed for 0 object(s) and 0 field(s)'
        }
      }

      63 |         JSON.stringify(serverlessSyncResult.error, null, 2),
      64 |       );
    > 65 |       console.error(
         |               ^
      66 |         chalk.red(' Serverless functions Sync failed:'),
      67 |         serverlessSyncResult.error,
      68 |       );

      at AppSyncCommand.synchronize (src/commands/app-sync.command.ts:65:15)
      at async AppSyncCommand.execute (src/commands/app-sync.command.ts:21:14)
      at async Object.<anonymous> (src/__tests__/e2e/applications-install-delete-reinstall.e2e-spec.ts:28:22)

```
This commit is contained in:
Paul Rastoin
2025-11-24 16:49:58 +01:00
committed by GitHub
parent 338e5cf74b
commit a735e3dfef
14 changed files with 62 additions and 275 deletions
+2 -9
View File
@@ -11,7 +11,7 @@
"cwd": "packages/twenty-cli",
"commands": ["rimraf dist", "tsc --project tsconfig.lib.json"]
},
"dependsOn": ["^build"]
"dependsOn": ["^build", "typecheck"]
},
"build": {
"executor": "nx:run-commands",
@@ -40,14 +40,7 @@
"command": "node dist/cli.js"
}
},
"typecheck": {
"executor": "nx:run-commands",
"options": {
"cwd": "packages/twenty-cli",
"command": "tsc --noEmit --project tsconfig.lib.json"
},
"dependsOn": ["build"]
},
"typecheck": {},
"lint": {
"options": {
"lintFilePatterns": ["{projectRoot}/src/**/*.{ts,json}"],
@@ -1,6 +1,6 @@
import { existsSync } from 'fs';
import { AppUninstallCommand } from 'src/commands/app-uninstall.command';
import { AppSyncCommand } from '../../commands/app-sync.command';
import { AppUninstallCommand } from '../../commands/app-uninstall.command';
import { COVERED_APPLICATION_FOLDERS } from './constants/covered-applications-folder.constant';
import { getTestedApplicationPath } from './utils/get-tested-application-path.util';
-1
View File
@@ -1,5 +1,4 @@
#!/usr/bin/env node
import chalk from 'chalk';
import { Command, CommanderError } from 'commander';
import { readFileSync } from 'fs';
@@ -1,9 +1,9 @@
import chalk from 'chalk';
import { CURRENT_EXECUTION_DIRECTORY } from '../constants/current-execution-directory';
import { ApiService } from '../services/api.service';
import { GenerateService } from '../services/generate.service';
import { ApiResponse } from '../types/config.types';
import { loadManifest } from '../utils/load-manifest';
import { GenerateService } from '../services/generate.service';
export class AppSyncCommand {
private apiService = new ApiService();
+10 -14
View File
@@ -1,16 +1,16 @@
import axios, { type AxiosInstance, type AxiosResponse } from 'axios';
import chalk from 'chalk';
import {
buildClientSchema,
getIntrospectionQuery,
printSchema,
} from 'graphql/index';
import {
type ApiResponse,
type AppManifest,
type PackageJson,
} from '../types/config.types';
import { ConfigService } from './config.service';
import {
buildClientSchema,
getIntrospectionQuery,
printSchema,
} from 'graphql/index';
export class ApiService {
private client: AxiosInstance;
@@ -125,8 +125,7 @@ export class ApiService {
if (response.data.errors) {
return {
success: false,
error:
response.data.errors[0]?.message || 'Failed to sync application',
error: response.data.errors[0],
};
}
@@ -136,13 +135,10 @@ export class ApiService {
message: `Successfully synced application: ${packageJson.name}`,
};
} catch (error) {
if (axios.isAxiosError(error) && error.response) {
return {
success: false,
error: error.response.data?.errors?.[0]?.message || error.message,
};
}
throw error;
return {
success: false,
error,
};
}
}
@@ -1,8 +1,8 @@
import chalk from 'chalk';
import { generate } from '@genql/cli';
import chalk from 'chalk';
import { join, resolve } from 'path';
import { ConfigService } from './config.service';
import { ApiService } from './api.service';
import { ConfigService } from './config.service';
export const GENERATED_FOLDER_NAME = 'generated';
@@ -38,7 +38,11 @@ export class GenerateService {
console.log(chalk.gray(`API URL: ${url}`));
console.log(chalk.gray(`Output: ${outputPath}`));
const { data: schema } = await this.apiService.getSchema();
const getSchemaResponse = await this.apiService.getSchema();
if (!getSchemaResponse.success) {
return;
}
const { data: schema } = getSchemaResponse;
await generate({
schema,
+12 -5
View File
@@ -96,9 +96,16 @@ export type ObjectManifest = {
fields: FieldMetadata[];
};
export interface ApiResponse<T = any> {
success: boolean;
data?: T;
error?: string;
export type SuccessfulApiResponse<T = unknown> = {
success: true;
data: T;
message?: string;
}
};
export type FailingApiResponse = {
success: false;
error?: unknown;
message?: string;
};
export type ApiResponse<T = unknown> =
| SuccessfulApiResponse<T>
| FailingApiResponse;
@@ -20,6 +20,7 @@ import {
isExportAssignment,
isFunctionExpression,
isIdentifier,
isImportDeclaration,
isNoSubstitutionTemplateLiteral,
isNumericLiteral,
isObjectLiteralExpression,
@@ -30,8 +31,8 @@ import {
isStringLiteralLike,
isTemplateExpression,
isVariableStatement,
isImportDeclaration,
} from 'typescript';
import { GENERATED_FOLDER_NAME } from '../services/generate.service';
import {
AppManifest,
Application,
@@ -42,10 +43,9 @@ import {
Sources,
} from '../types/config.types';
import { findPathFile } from '../utils/find-path-file';
import { getTsProgramAndDiagnostics } from '../utils/get-ts-program-and-diagnostics';
import { parseJsoncFile, parseTextFile } from '../utils/jsonc-parser';
import { formatAndWarnTsDiagnostics } from './format-and-warn-ts-diagnostics';
import { getTsProgramAndDiagnostics } from '../utils/get-ts-program-and-diagnostics';
import { GENERATED_FOLDER_NAME } from '../services/generate.service';
type JSONValue =
| string