From 52fe21c04bcc3aebc430091679128ebe711d13f9 Mon Sep 17 00:00:00 2001 From: martmull Date: Tue, 10 Feb 2026 11:48:10 +0100 Subject: [PATCH] Fix ci + improvements (#17795) as title --- .github/workflows/ci-sdk.yaml | 20 ++------ packages/twenty-sdk/project.json | 16 +----- .../manifest.integration.spec.ts | 10 ++-- ...tions-install-delete-reinstall.e2e-spec.ts | 35 +++++++------ .../app-dev/app-dev.integration.spec.ts | 15 ++---- .../app-dev/app-dev.integration.spec.ts | 13 ++--- .../src/cli/__tests__/constants/setupTest.ts | 22 +++++--- .../src/cli/__tests__/constants/testConfig.ts | 2 +- .../utils/run-app-dev-in-process.util.ts | 37 ++++++++++++++ .../integration/utils/run-app-dev.util.ts | 22 -------- .../integration/utils/run-cli-command.util.ts | 9 ++-- .../integration/utils/setup-app-dev-mocks.ts | 33 ++++++++++++ .../src/cli/commands/app-command.ts | 5 +- .../src/cli/commands/app/app-dev.ts | 30 +++++------ .../logic-function/logic-function-execute.ts | 2 +- .../cli/utilities/config/config-service.ts | 5 +- .../cli/utilities/config/get-config-path.ts | 12 +++++ .../twenty-sdk/vitest.integration.config.ts | 5 +- packages/twenty-server/.env.test | 1 + packages/twenty-server/project.json | 2 +- .../utils/copy-executor.ts | 13 ++++- .../utils/get-executor-file-path.ts | 12 ----- .../dtos/create-logic-function.input.ts | 50 ++----------------- ...ction-input-to-flat-logic-function.util.ts | 2 +- .../utils/get-code-step-handler-path.util.ts | 12 ----- .../get-code-step-seed-project-files.util.ts | 45 ----------------- 26 files changed, 182 insertions(+), 248 deletions(-) create mode 100644 packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev-in-process.util.ts delete mode 100644 packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev.util.ts create mode 100644 packages/twenty-sdk/src/cli/__tests__/integration/utils/setup-app-dev-mocks.ts create mode 100644 packages/twenty-sdk/src/cli/utilities/config/get-config-path.ts delete mode 100644 packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/get-executor-file-path.ts delete mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-handler-path.util.ts delete mode 100644 packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-seed-project-files.util.ts diff --git a/.github/workflows/ci-sdk.yaml b/.github/workflows/ci-sdk.yaml index 0bf71bb9491..a4a368b6edb 100644 --- a/.github/workflows/ci-sdk.yaml +++ b/.github/workflows/ci-sdk.yaml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - task: [lint, typecheck, test:unit, storybook:build, storybook:test] + task: [lint, typecheck, test:unit, storybook:build, storybook:test, test:integration] steps: - name: Cancel Previous Runs uses: styfle/cancel-workflow-action@0.11.0 @@ -47,7 +47,7 @@ jobs: with: tag: scope:sdk tasks: ${{ matrix.task }} - sdk-e2e-integration-test: + sdk-e2e-test: timeout-minutes: 30 runs-on: ubuntu-latest-8-cores needs: [changed-files-check, sdk-test] @@ -82,23 +82,9 @@ jobs: uses: ./.github/actions/yarn-install - name: Build run: npx nx build twenty-sdk - - name: Server / Append billing config to .env.test - working-directory: packages/twenty-server - run: | - echo "" >> .env.test - echo "IS_BILLING_ENABLED=true" >> .env.test - echo "BILLING_STRIPE_API_KEY=test-api-key" >> .env.test - echo "BILLING_STRIPE_BASE_PLAN_PRODUCT_ID=test-base-plan-product-id" >> .env.test - echo "BILLING_STRIPE_WEBHOOK_SECRET=test-webhook-secret" >> .env.test - echo "BILLING_PLAN_REQUIRED_LINK=http://localhost:3001/stripe-redirection" >> .env.test - name: Server / Create Test DB run: | PGPASSWORD=postgres psql -h localhost -p 5432 -U postgres -d postgres -c 'CREATE DATABASE "test";' - - name: SDK / Run integration tests - uses: ./.github/actions/nx-affected - with: - tag: scope:sdk - tasks: test:integration - name: SDK / Run e2e Tests uses: ./.github/actions/nx-affected with: @@ -108,7 +94,7 @@ jobs: if: always() && !cancelled() timeout-minutes: 5 runs-on: ubuntu-latest - needs: [changed-files-check, sdk-test, sdk-e2e-integration-test] + needs: [changed-files-check, sdk-test, sdk-e2e-test] steps: - name: Fail job if any needs failed if: contains(needs.*.result, 'failure') diff --git a/packages/twenty-sdk/project.json b/packages/twenty-sdk/project.json index bcde0a5e194..dd9c11b3ab4 100644 --- a/packages/twenty-sdk/project.json +++ b/packages/twenty-sdk/project.json @@ -63,20 +63,8 @@ "executor": "nx:run-commands", "options": { "cwd": "packages/twenty-sdk", - "command": "npx wait-on http://localhost:3000/healthz --timeout 600000 --interval 1000 --log && npx vitest run --config vitest.integration.config.ts" - }, - "parallel": false, - "dependsOn": [ - "build", - { - "target": "database:reset", - "projects": "twenty-server" - }, - { - "target": "start:ci-if-needed", - "projects": "twenty-server" - } - ] + "command": "npx vitest run --config vitest.integration.config.ts" + } }, "test:e2e": { "executor": "nx:run-commands", diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/invalid-app/__integration__/manifest.integration.spec.ts b/packages/twenty-sdk/src/cli/__tests__/apps/invalid-app/__integration__/manifest.integration.spec.ts index 2908fdf1bfa..1b8891a7fe1 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/invalid-app/__integration__/manifest.integration.spec.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/invalid-app/__integration__/manifest.integration.spec.ts @@ -1,17 +1,19 @@ -import { runAppDev } from '@/cli/__tests__/integration/utils/run-app-dev.util'; +import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util'; import * as fs from 'fs-extra'; import { join } from 'path'; import { OUTPUT_DIR } from 'twenty-shared/application'; const APP_PATH = join(__dirname, '..'); -const MANIFEST_OUTPUT_PATH = join(APP_PATH, OUTPUT_DIR, 'ioi', 'manifest.json'); +const MANIFEST_OUTPUT_PATH = join(APP_PATH, OUTPUT_DIR, 'manifest.json'); describe('invalid-app manifest', () => { it('should fail to build manifest due to duplicate universalIdentifier', async () => { - const result = await runAppDev({ appPath: APP_PATH, timeout: 10000 }); + const result = await runAppDevInProcess({ + appPath: APP_PATH, + timeout: 10000, + }); expect(result.success).toBe(false); - expect(result.output).toContain('Duplicate universal identifiers'); const manifestExists = await fs.pathExists(MANIFEST_OUTPUT_PATH); diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__e2e__/applications-install-delete-reinstall.e2e-spec.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__e2e__/applications-install-delete-reinstall.e2e-spec.ts index 33995f9bb80..69926ef5fa3 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__e2e__/applications-install-delete-reinstall.e2e-spec.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__e2e__/applications-install-delete-reinstall.e2e-spec.ts @@ -1,47 +1,52 @@ -import { runAppDev } from '@/cli/__tests__/integration/utils/run-app-dev.util'; import { OUTPUT_DIR } from 'twenty-shared/application'; -import { AppUninstallCommand } from '@/cli/commands/app/app-uninstall'; import { existsSync } from 'fs'; import { join, resolve } from 'path'; import { inspect } from 'util'; +import { runCliCommand } from '@/cli/__tests__/integration/utils/run-cli-command.util'; inspect.defaultOptions.depth = 10; describe('Application: install delete and reinstall rich-app', () => { const applicationName = 'rich-app'; - const deleteCommand = new AppUninstallCommand(); const appPath = resolve(__dirname, '../'); beforeAll(async () => { expect(existsSync(appPath)).toBe(true); - }); - afterAll(async () => { - const result = await deleteCommand.execute({ - appPath, - askForConfirmation: false, + const result = await runCliCommand({ + command: 'auth:status', + args: [appPath], + timeout: 5_000, + waitForOutput: '✓ Valid', }); expect(result.success).toBe(true); }); it(`should successfully install ${applicationName} application`, async () => { - await runAppDev({ appPath }); + await runCliCommand({ + command: 'app:dev', + args: [appPath], + waitForOutput: '✓ Synced', + }); expect(existsSync(join(appPath, OUTPUT_DIR, 'manifest.json'))).toBe(true); }); it(`should successfully delete ${applicationName} application`, async () => { - const result = await deleteCommand.execute({ - appPath, - askForConfirmation: false, + await runCliCommand({ + command: 'app:uninstall', + args: [appPath, '-y'], + waitForOutput: 'Application uninstalled successfully', }); - - expect(result.success).toBe(true); }); it(`should successfully re-install ${applicationName} application`, async () => { - await runAppDev({ appPath }); + await runCliCommand({ + command: 'app:dev', + args: [appPath], + waitForOutput: '✓ Synced', + }); expect(existsSync(join(appPath, OUTPUT_DIR, 'manifest.json'))).toBe(true); }); diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/app-dev.integration.spec.ts b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/app-dev.integration.spec.ts index 7812462a40d..b5df46e53e5 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/app-dev.integration.spec.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/rich-app/__integration__/app-dev/app-dev.integration.spec.ts @@ -1,25 +1,18 @@ -import { runAppDev } from '@/cli/__tests__/integration/utils/run-app-dev.util'; -import { type RunCliCommandResult } from '@/cli/__tests__/integration/utils/run-cli-command.util'; +import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util'; import { join } from 'path'; -import { defineConsoleOutputTests } from './tests/console-output.tests'; -import { defineManifestTests } from './tests/manifest.tests'; import { defineEntitiesTests } from './tests/entities.tests'; +import { defineManifestTests } from './tests/manifest.tests'; const APP_PATH = join(__dirname, '../..'); describe('rich-app app:dev', () => { - let result: RunCliCommandResult; - beforeAll(async () => { - result = await runAppDev({ appPath: APP_PATH }); - if (!result.success) { - console.log(result.output.slice(undefined, 20_000)); - } + const result = await runAppDevInProcess({ appPath: APP_PATH }); + expect(result.success).toBe(true); }, 60000); - defineConsoleOutputTests(() => result); defineManifestTests(APP_PATH); defineEntitiesTests(APP_PATH); }); diff --git a/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/app-dev.integration.spec.ts b/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/app-dev.integration.spec.ts index 7de175854cf..2a9935f1a36 100644 --- a/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/app-dev.integration.spec.ts +++ b/packages/twenty-sdk/src/cli/__tests__/apps/root-app/__integration__/app-dev/app-dev.integration.spec.ts @@ -1,8 +1,6 @@ import { join } from 'path'; -import { runAppDev } from '@/cli/__tests__/integration/utils/run-app-dev.util'; -import { type RunCliCommandResult } from '@/cli/__tests__/integration/utils/run-cli-command.util'; -import { defineConsoleOutputTests } from './tests/console-output.tests'; +import { runAppDevInProcess } from '@/cli/__tests__/integration/utils/run-app-dev-in-process.util'; import { defineFrontComponentsTests } from './tests/front-components.tests'; import { defineLogicFunctionsTests } from './tests/logic-functions.tests'; import { defineManifestTests } from './tests/manifest.tests'; @@ -10,17 +8,12 @@ import { defineManifestTests } from './tests/manifest.tests'; const APP_PATH = join(__dirname, '../..'); describe('root-app app:dev', () => { - let result: RunCliCommandResult; - beforeAll(async () => { - result = await runAppDev({ appPath: APP_PATH }); - if (!result.success) { - console.log(result.output.slice(undefined, 20_000)); - } + const result = await runAppDevInProcess({ appPath: APP_PATH }); + expect(result.success).toBe(true); }, 60000); - defineConsoleOutputTests(() => result); defineManifestTests(APP_PATH); defineLogicFunctionsTests(APP_PATH); defineFrontComponentsTests(APP_PATH); diff --git a/packages/twenty-sdk/src/cli/__tests__/constants/setupTest.ts b/packages/twenty-sdk/src/cli/__tests__/constants/setupTest.ts index d2458df652a..62e5df69fa1 100644 --- a/packages/twenty-sdk/src/cli/__tests__/constants/setupTest.ts +++ b/packages/twenty-sdk/src/cli/__tests__/constants/setupTest.ts @@ -1,11 +1,19 @@ -import { ConfigService } from '@/cli/utilities/config/config-service'; import { testConfig } from '@/cli/__tests__/constants/testConfig'; -import { vi, beforeAll, afterAll } from 'vitest'; +import { getConfigPath } from '@/cli/utilities/config/get-config-path'; +import * as fs from 'fs-extra'; +import * as path from 'path'; +import { beforeAll } from 'vitest'; -beforeAll(() => { - vi.spyOn(ConfigService.prototype, 'getConfig').mockResolvedValue(testConfig); -}); +const testConfigPath = getConfigPath(); -afterAll(() => { - vi.restoreAllMocks(); +beforeAll(async () => { + await fs.ensureDir(path.dirname(testConfigPath)); + + const configFile = { + profiles: { + default: testConfig, + }, + }; + + await fs.writeFile(testConfigPath, JSON.stringify(configFile, null, 2)); }); diff --git a/packages/twenty-sdk/src/cli/__tests__/constants/testConfig.ts b/packages/twenty-sdk/src/cli/__tests__/constants/testConfig.ts index 4f4b9786ac9..a6216bf6fb9 100644 --- a/packages/twenty-sdk/src/cli/__tests__/constants/testConfig.ts +++ b/packages/twenty-sdk/src/cli/__tests__/constants/testConfig.ts @@ -1,7 +1,7 @@ import { type TwentyConfig } from '@/cli/utilities/config/config-service'; export const testConfig: TwentyConfig = { - apiUrl: 'http://localhost:3000', + apiUrl: 'http://apple.localhost:3000', apiKey: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ1c2VySWQiOiIyMDIwMjAyMC1lNmI1LTQ2ODAtOGEzMi1iODIwOTczNzE1NmIiLCJ3b3Jrc3BhY2VJZCI6IjIwMjAyMDIwLTFjMjUtNGQwMi1iZjI1LTZhZWNjZjdlYTQxOSIsIndvcmtzcGFjZU1lbWJlcklkIjoiMjAyMDIwMjAtNDYzZi00MzViLTgyOGMtMTA3ZTAwN2EyNzExIiwidXNlcldvcmtzcGFjZUlkIjoiMjAyMDIwMjAtMWU3Yy00M2Q5LWE1ZGItNjg1YjUwNjlkODE2IiwidHlwZSI6IkFDQ0VTUyIsImF1dGhQcm92aWRlciI6InBhc3N3b3JkIiwiaWF0IjoxNzUxMjgxNzA0LCJleHAiOjIwNjY4NTc3MDR9.HMGqCsVlOAPVUBhKSGlD1X86VoHKt4LIUtET3CGIdik', }; diff --git a/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev-in-process.util.ts b/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev-in-process.util.ts new file mode 100644 index 00000000000..56b32904445 --- /dev/null +++ b/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev-in-process.util.ts @@ -0,0 +1,37 @@ +import { AppDevCommand } from '@/cli/commands/app/app-dev'; +import * as fs from 'fs-extra'; +import { join } from 'path'; +import { OUTPUT_DIR } from 'twenty-shared/application'; + +export type RunAppDevResult = { + success: boolean; +}; + +export const runAppDevInProcess = async (options: { + appPath: string; + timeout?: number; +}): Promise => { + const { appPath, timeout = 30_000 } = options; + const manifestPath = join(appPath, OUTPUT_DIR, 'manifest.json'); + + const command = new AppDevCommand(); + + await command.execute({ appPath }); + + const startTime = Date.now(); + + while (Date.now() - startTime < timeout) { + if (await fs.pathExists(manifestPath)) { + // Small delay to let any pending writes finish + await new Promise((resolve) => setTimeout(resolve, 500)); + await command.close(); + + return { success: true }; + } + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + await command.close(); + + return { success: false }; +}; diff --git a/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev.util.ts b/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev.util.ts deleted file mode 100644 index 713f1eaabd5..00000000000 --- a/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-app-dev.util.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - runCliCommand, - type RunCliCommandResult, -} from './run-cli-command.util'; - -export type RunAppDevOptions = { - appPath: string; - timeout?: number; -}; - -export const runAppDev = ( - options: RunAppDevOptions, -): Promise => { - const { appPath, timeout = 60_000 } = options; - - return runCliCommand({ - command: 'app:dev', - args: [appPath], - waitForOutput: ['✓ Synced'], - timeout, - }); -}; diff --git a/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-cli-command.util.ts b/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-cli-command.util.ts index d96236b3fad..835d1fa6e7f 100644 --- a/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-cli-command.util.ts +++ b/packages/twenty-sdk/src/cli/__tests__/integration/utils/run-cli-command.util.ts @@ -23,7 +23,7 @@ export type RunCliCommandResult = { export const runCliCommand = ( options: RunCliCommandOptions, ): Promise => { - const { command, args = [], waitForOutput, timeout = 60_000 } = options; + const { command, args = [], waitForOutput, timeout = 30_000 } = options; return new Promise((resolve) => { // Run from CLI directory to use twenty-sdk's tsconfig paths @@ -43,6 +43,7 @@ export const runCliCommand = ( let output = ''; const timeoutId = setTimeout(() => { child.kill(); + console.log(`RunCliCommand ${command} timeout after ${timeout / 1_000}s`); resolve({ success: false, output }); }, timeout); @@ -76,11 +77,7 @@ export const runCliCommand = ( child.on('close', (code) => { clearTimeout(timeoutId); - if (waitForOutputs.length === 0) { - resolve({ success: code === 0, output }); - } else { - resolve({ success: false, output }); - } + resolve({ success: code === 0, output }); }); child.on('error', () => { diff --git a/packages/twenty-sdk/src/cli/__tests__/integration/utils/setup-app-dev-mocks.ts b/packages/twenty-sdk/src/cli/__tests__/integration/utils/setup-app-dev-mocks.ts new file mode 100644 index 00000000000..3ea5fa3fef5 --- /dev/null +++ b/packages/twenty-sdk/src/cli/__tests__/integration/utils/setup-app-dev-mocks.ts @@ -0,0 +1,33 @@ +import { vi } from 'vitest'; + +const mockApiService = { + validateAuth: vi.fn().mockResolvedValue({ authValid: true, serverUp: true }), + checkApplicationExist: vi + .fn() + .mockResolvedValue({ success: true, data: false }), + createApplication: vi + .fn() + .mockResolvedValue({ success: true, data: { id: 'mock-id' } }), + syncApplication: vi.fn().mockResolvedValue({ success: true, data: true }), + uploadFile: vi.fn().mockResolvedValue({ success: true, data: true }), +}; + +vi.mock('@/cli/utilities/api/api-service', () => ({ + ApiService: class { + validateAuth = mockApiService.validateAuth; + checkApplicationExist = mockApiService.checkApplicationExist; + createApplication = mockApiService.createApplication; + syncApplication = mockApiService.syncApplication; + uploadFile = mockApiService.uploadFile; + }, +})); + +vi.mock('@/cli/utilities/file/file-uploader', () => ({ + FileUploader: class { + uploadFile = vi.fn().mockResolvedValue({ success: true, data: true }); + }, +})); + +vi.mock('@/cli/utilities/dev/dev-ui', () => ({ + renderDevUI: vi.fn().mockResolvedValue({ unmount: vi.fn() }), +})); diff --git a/packages/twenty-sdk/src/cli/commands/app-command.ts b/packages/twenty-sdk/src/cli/commands/app-command.ts index ca040d50f0f..8404406058d 100644 --- a/packages/twenty-sdk/src/cli/commands/app-command.ts +++ b/packages/twenty-sdk/src/cli/commands/app-command.ts @@ -79,11 +79,12 @@ export const registerCommands = (program: Command): void => { program .command('app:uninstall [appPath]') .description('Uninstall application from Twenty') - .action(async (appPath?: string) => { + .option('-y, --yes', 'Skip confirmation prompt') + .action(async (appPath?: string, options?: { yes?: boolean }) => { try { const result = await uninstallCommand.execute({ appPath: formatPath(appPath), - askForConfirmation: true, + askForConfirmation: !options?.yes, }); process.exit(result.success ? 0 : 1); } catch { diff --git a/packages/twenty-sdk/src/cli/commands/app/app-dev.ts b/packages/twenty-sdk/src/cli/commands/app/app-dev.ts index d781fd530ed..41ef6b256dd 100644 --- a/packages/twenty-sdk/src/cli/commands/app/app-dev.ts +++ b/packages/twenty-sdk/src/cli/commands/app/app-dev.ts @@ -31,6 +31,18 @@ export class AppDevCommand { private uiStateManager: DevUiStateManager | null = null; private unmountUI: (() => void) | null = null; + async close(): Promise { + this.unmountUI?.(); + + await Promise.all([ + this.manifestWatcher?.close(), + this.logicFunctionsWatcher?.close(), + this.frontComponentsWatcher?.close(), + this.assetWatcher?.close(), + this.dependencyWatcher?.close(), + ]); + } + async execute(options: AppDevOptions): Promise { this.appPath = options.appPath ?? CURRENT_EXECUTION_DIRECTORY; @@ -163,21 +175,9 @@ export class AppDevCommand { } private setupGracefulShutdown(): void { - const shutdown = async () => { - this.unmountUI?.(); + const shutdown = () => void this.close().then(() => process.exit(0)); - await Promise.all([ - this.manifestWatcher?.close(), - this.logicFunctionsWatcher?.close(), - this.frontComponentsWatcher?.close(), - this.assetWatcher?.close(), - this.dependencyWatcher?.close(), - ]); - - process.exit(0); - }; - - process.on('SIGINT', () => void shutdown()); - process.on('SIGTERM', () => void shutdown()); + process.on('SIGINT', shutdown); + process.on('SIGTERM', shutdown); } } diff --git a/packages/twenty-sdk/src/cli/commands/logic-function/logic-function-execute.ts b/packages/twenty-sdk/src/cli/commands/logic-function/logic-function-execute.ts index 6ebd5846fd8..dd914f4f6de 100644 --- a/packages/twenty-sdk/src/cli/commands/logic-function/logic-function-execute.ts +++ b/packages/twenty-sdk/src/cli/commands/logic-function/logic-function-execute.ts @@ -79,7 +79,7 @@ export class LogicFunctionExecuteCommand { } else { console.log( chalk.yellow( - 'No functions found for this application. Have you synced your app with `yarn app:sync`?', + 'No functions found for this application. Have you synced your app with `yarn app:dev`?', ), ); } diff --git a/packages/twenty-sdk/src/cli/utilities/config/config-service.ts b/packages/twenty-sdk/src/cli/utilities/config/config-service.ts index bbfe70589aa..8fa6bc34758 100644 --- a/packages/twenty-sdk/src/cli/utilities/config/config-service.ts +++ b/packages/twenty-sdk/src/cli/utilities/config/config-service.ts @@ -1,7 +1,8 @@ import * as fs from 'fs-extra'; -import * as os from 'os'; import * as path from 'path'; +import { getConfigPath } from '@/cli/utilities/config/get-config-path'; + export type TwentyConfig = { apiUrl: string; apiKey?: string; @@ -19,7 +20,7 @@ export class ConfigService { private static activeWorkspace = DEFAULT_WORKSPACE_NAME; constructor() { - this.configPath = path.join(os.homedir(), '.twenty', 'config.json'); + this.configPath = getConfigPath(); } static setActiveWorkspace(name?: string) { diff --git a/packages/twenty-sdk/src/cli/utilities/config/get-config-path.ts b/packages/twenty-sdk/src/cli/utilities/config/get-config-path.ts new file mode 100644 index 00000000000..e0ff8143dfa --- /dev/null +++ b/packages/twenty-sdk/src/cli/utilities/config/get-config-path.ts @@ -0,0 +1,12 @@ +import * as os from 'os'; +import * as path from 'path'; + +const TEST_CONFIG_DIR = path.join(os.tmpdir(), '.twenty-sdk-test'); + +export const getConfigPath = (): string => { + if (process.env.NODE_ENV === 'test') { + return path.join(TEST_CONFIG_DIR, 'config.json'); + } + + return path.join(os.homedir(), '.twenty', 'config.json'); +}; diff --git a/packages/twenty-sdk/vitest.integration.config.ts b/packages/twenty-sdk/vitest.integration.config.ts index b5dc70c60f1..b2fde09efe1 100644 --- a/packages/twenty-sdk/vitest.integration.config.ts +++ b/packages/twenty-sdk/vitest.integration.config.ts @@ -18,6 +18,9 @@ export default defineConfig({ truncateThreshold: 0, }, fileParallelism: false, - setupFiles: ['src/cli/__tests__/constants/setupTest.ts'], + setupFiles: [ + 'src/cli/__tests__/constants/setupTest.ts', + 'src/cli/__tests__/integration/utils/setup-app-dev-mocks.ts', + ], }, }); diff --git a/packages/twenty-server/.env.test b/packages/twenty-server/.env.test index 2f5b1e9b5d2..41ac41cb5ae 100644 --- a/packages/twenty-server/.env.test +++ b/packages/twenty-server/.env.test @@ -2,6 +2,7 @@ PG_DATABASE_URL=postgres://postgres:postgres@localhost:5432/test REDIS_URL=redis://localhost:6379 APP_SECRET=replace_me_with_a_random_string +ACCESS_TOKEN_SECRET=replace_me_with_a_random_string SIGN_IN_PREFILLED=true EXCEPTION_HANDLER_DRIVER=CONSOLE TELEMETRY_ENABLED=false diff --git a/packages/twenty-server/project.json b/packages/twenty-server/project.json index 4b9c754e287..25159960a76 100644 --- a/packages/twenty-server/project.json +++ b/packages/twenty-server/project.json @@ -65,7 +65,7 @@ "dependsOn": ["build"], "options": { "cwd": "packages/twenty-server", - "command": "curl -f -s http://localhost:3000/healthz > /dev/null 2>&1 && echo '✅ Server already running' || (echo '🚀 Server not running, starting...' && nohup nest start > server.log 2>&1 &)" + "command": "curl -f -s http://localhost:3000/healthz > /dev/null 2>&1 && echo '✅ Server already running' || (echo '🚀 Server not running, starting...' && nohup nest start &)" } }, "start:debug": { diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor.ts index 3229ae0925c..53b5a473a7c 100644 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor.ts +++ b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/copy-executor.ts @@ -1,12 +1,21 @@ import { promises as fs } from 'fs'; +import { resolve, join } from 'path'; -import { getExecutorFilePath } from 'src/engine/core-modules/logic-function/logic-function-drivers/utils/get-executor-file-path'; +import { ASSET_PATH } from 'src/constants/assets-path'; + +const EXECUTOR_FILE_PATH = resolve( + __dirname, + join( + ASSET_PATH, + `engine/core-modules/logic-function/logic-function-drivers/constants/executor`, + ), +); export const copyExecutor = async (buildDirectory: string) => { await fs.mkdir(buildDirectory, { recursive: true, }); - await fs.cp(getExecutorFilePath(), buildDirectory, { + await fs.cp(EXECUTOR_FILE_PATH, buildDirectory, { recursive: true, }); }; diff --git a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/get-executor-file-path.ts b/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/get-executor-file-path.ts deleted file mode 100644 index 8b83a0e4f5a..00000000000 --- a/packages/twenty-server/src/engine/core-modules/logic-function/logic-function-drivers/utils/get-executor-file-path.ts +++ /dev/null @@ -1,12 +0,0 @@ -import path from 'path'; - -import { ASSET_PATH } from 'src/constants/assets-path'; - -export const getExecutorFilePath = (): string => { - const baseTypescriptProjectPath = path.join( - ASSET_PATH, - `engine/core-modules/logic-function/logic-function-drivers/constants/executor`, - ); - - return path.resolve(__dirname, baseTypescriptProjectPath); -}; diff --git a/packages/twenty-server/src/engine/metadata-modules/logic-function/dtos/create-logic-function.input.ts b/packages/twenty-server/src/engine/metadata-modules/logic-function/dtos/create-logic-function.input.ts index 01dd4794abb..dcba86ea20e 100644 --- a/packages/twenty-server/src/engine/metadata-modules/logic-function/dtos/create-logic-function.input.ts +++ b/packages/twenty-server/src/engine/metadata-modules/logic-function/dtos/create-logic-function.input.ts @@ -1,15 +1,6 @@ import { Field, HideField, InputType } from '@nestjs/graphql'; -import { - IsBoolean, - IsNotEmpty, - IsNumber, - IsObject, - IsOptional, - IsString, - Max, - Min, -} from 'class-validator'; +import { IsObject, IsOptional, IsString } from 'class-validator'; import graphqlTypeJson from 'graphql-type-json'; import { CronTriggerSettings, @@ -18,35 +9,10 @@ import { } from 'twenty-shared/application'; import type { JsonbProperty } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/jsonb-property.type'; +import { CreateDefaultLogicFunctionInput } from 'src/engine/metadata-modules/logic-function/dtos/create-default-logic-function.input'; @InputType() -export class CreateLogicFunctionInput { - @IsString() - @IsNotEmpty() - @Field() - name: string; - - @IsString() - @IsOptional() - @Field({ nullable: true }) - description?: string; - - @IsNumber() - @Field({ nullable: true }) - @Min(1) - @Max(900) - @IsOptional() - timeoutSeconds?: number; - - @HideField() - applicationId: string; - - @HideField() - universalIdentifier?: string; - - @HideField() - id: string; - +export class CreateLogicFunctionInput extends CreateDefaultLogicFunctionInput { @HideField() checksum: string; @@ -62,16 +28,6 @@ export class CreateLogicFunctionInput { @Field({ nullable: false }) builtHandlerPath: string; - @Field(() => graphqlTypeJson, { nullable: true }) - @IsObject() - @IsOptional() - toolInputSchema?: object; - - @IsBoolean() - @Field({ nullable: true }) - @IsOptional() - isTool?: boolean; - @IsObject() @Field(() => graphqlTypeJson, { nullable: true }) @IsOptional() diff --git a/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-create-logic-function-input-to-flat-logic-function.util.ts b/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-create-logic-function-input-to-flat-logic-function.util.ts index 2ee67631476..a7a26e2fab9 100644 --- a/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-create-logic-function-input-to-flat-logic-function.util.ts +++ b/packages/twenty-server/src/engine/metadata-modules/logic-function/utils/from-create-logic-function-input-to-flat-logic-function.util.ts @@ -19,7 +19,7 @@ export const fromCreateLogicFunctionInputToFlatLogicFunction = ({ workspaceId, ownerFlatApplication, }: FromCreateLogicFunctionInputToFlatLogicFunctionArgs): FlatLogicFunction => { - const id = rawCreateLogicFunctionInput.id; + const id = rawCreateLogicFunctionInput.id ?? v4(); const currentDate = new Date(); const sourceHandlerPath = rawCreateLogicFunctionInput.sourceHandlerPath; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-handler-path.util.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-handler-path.util.ts deleted file mode 100644 index 6ed88af8d80..00000000000 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-handler-path.util.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { dirname } from 'path'; - -export const getLogicFunctionBaseFolderPath = (handlerPath: string): string => { - return dirname(dirname(handlerPath)); -}; - -export const getRelativePathFromBase = ( - handlerPath: string, - baseFolderPath: string, -): string => { - return handlerPath.replace(`${baseFolderPath}/`, ''); -}; diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-seed-project-files.util.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-seed-project-files.util.ts deleted file mode 100644 index 3726aa87705..00000000000 --- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/code-step/utils/get-code-step-seed-project-files.util.ts +++ /dev/null @@ -1,45 +0,0 @@ -import fs from 'fs/promises'; -import path from 'path'; - -import { ASSET_PATH } from 'src/constants/assets-path'; - -export type CodeStepSeedProjectFile = { - name: string; - path: string; - content: Buffer; -}; - -const getAllFiles = async ( - rootDir: string, - dir: string = rootDir, - files: CodeStepSeedProjectFile[] = [], -): Promise => { - const dirEntries = await fs.readdir(dir, { withFileTypes: true }); - - for (const entry of dirEntries) { - const fullPath = path.join(dir, entry.name); - - if (entry.isDirectory()) { - await getAllFiles(rootDir, fullPath, files); - } else { - files.push({ - path: path.relative(rootDir, dir), - name: entry.name, - content: await fs.readFile(fullPath), - }); - } - } - - return files; -}; - -export const getCodeStepSeedProjectFiles = async (): Promise< - CodeStepSeedProjectFile[] -> => { - const seedProjectPath = path.join( - ASSET_PATH, - 'modules/workflow/workflow-builder/workflow-version-step/code-step/constants/seed-project', - ); - - return getAllFiles(seedProjectPath); -};