2094 extensibility define postinstall orand preinstall function to run in application (#18037)

- add a new optional key `postInstallLogicFunctionUniversalIdentifier`
in applicationConfig
- seed postInstall function in create-twenty-app
- update execute:function options
- update doc
This commit is contained in:
martmull
2026-02-18 16:38:22 +01:00
committed by GitHub
parent 618df704e6
commit 53c314d0fa
11 changed files with 155 additions and 15 deletions
@@ -129,6 +129,7 @@ export const registerCommands = (program: Command): void => {
program
.command('function:execute [appPath]')
.option('--postInstall', 'Execute post-install logic function if defined')
.option(
'-p, --payload <payload>',
'JSON payload to send to the function',
@@ -147,15 +148,20 @@ export const registerCommands = (program: Command): void => {
async (
appPath?: string,
options?: {
postInstall?: boolean;
payload?: string;
functionUniversalIdentifier?: string;
functionName?: string;
},
) => {
if (!options?.functionUniversalIdentifier && !options?.functionName) {
if (
!options?.postInstall &&
!options?.functionUniversalIdentifier &&
!options?.functionName
) {
console.error(
chalk.red(
'Error: Either --functionName (-n) or --functionUniversalIdentifier (-u) is required.',
'Error: Either --postInstall or --functionName (-n) or --functionUniversalIdentifier (-u) is required.',
),
);
process.exit(1);
@@ -3,7 +3,7 @@ import { type ApiResponse } from '@/cli/utilities/api/api-response-type';
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
import chalk from 'chalk';
import inquirer from 'inquirer';
import { buildManifest } from '@/cli/utilities/build/manifest/manifest-build';
import { readManifestFromFile } from '@/cli/utilities/build/manifest/manifest-reader';
export class AppUninstallCommand {
private apiService = new ApiService();
@@ -25,7 +25,7 @@ export class AppUninstallCommand {
process.exit(1);
}
const { manifest } = await buildManifest(appPath);
const manifest = await readManifestFromFile(appPath);
if (!manifest) {
return { success: false, error: 'Build failed' };
@@ -3,18 +3,20 @@ import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-exec
import chalk from 'chalk';
import { type Manifest } from 'twenty-shared/application';
import { isDefined } from 'twenty-shared/utils';
import { buildManifest } from '@/cli/utilities/build/manifest/manifest-build';
import { readManifestFromFile } from '@/cli/utilities/build/manifest/manifest-reader';
export class LogicFunctionExecuteCommand {
private apiService = new ApiService();
async execute({
appPath = CURRENT_EXECUTION_DIRECTORY,
postInstall = false,
functionUniversalIdentifier,
functionName,
payload = '{}',
}: {
appPath?: string;
postInstall?: boolean;
functionUniversalIdentifier?: string;
functionName?: string;
payload?: string;
@@ -30,7 +32,7 @@ export class LogicFunctionExecuteCommand {
process.exit(1);
}
const { manifest } = await buildManifest(appPath);
const manifest = await readManifestFromFile(appPath);
if (!manifest) {
console.error(chalk.red('Failed to build manifest.'));
@@ -54,6 +56,12 @@ export class LogicFunctionExecuteCommand {
);
const targetFunction = appFunctions.find((fn) => {
if (postInstall) {
return (
fn.universalIdentifier ===
manifest.application.postInstallLogicFunctionUniversalIdentifier
);
}
if (functionUniversalIdentifier) {
return fn.universalIdentifier === functionUniversalIdentifier;
}
@@ -64,7 +72,9 @@ export class LogicFunctionExecuteCommand {
});
if (!targetFunction) {
const identifier = functionUniversalIdentifier || functionName;
const identifier = postInstall
? 'post install'
: functionUniversalIdentifier || functionName;
console.error(
chalk.red(`Function "${identifier}" not found in application.`),
);
@@ -1,7 +1,7 @@
import { ApiService } from '@/cli/utilities/api/api-service';
import { CURRENT_EXECUTION_DIRECTORY } from '@/cli/utilities/config/current-execution-directory';
import chalk from 'chalk';
import { buildManifest } from '@/cli/utilities/build/manifest/manifest-build';
import { readManifestFromFile } from '@/cli/utilities/build/manifest/manifest-reader';
export class LogicFunctionLogsCommand {
private apiService = new ApiService();
@@ -16,7 +16,7 @@ export class LogicFunctionLogsCommand {
functionName?: string;
}): Promise<void> {
try {
const { manifest } = await buildManifest(appPath);
const manifest = await readManifestFromFile(appPath);
if (!manifest) {
process.exit(1);
@@ -0,0 +1,21 @@
import * as fs from 'fs-extra';
import path from 'path';
import { type Manifest, OUTPUT_DIR } from 'twenty-shared/application';
import { buildManifest } from '@/cli/utilities/build/manifest/manifest-build';
export const readManifestFromFile = async (
appPath: string,
): Promise<Manifest | null> => {
const outputDir = path.join(appPath, OUTPUT_DIR);
await fs.ensureDir(outputDir);
const manifestPath = path.join(outputDir, 'manifest.json');
if (!(await fs.pathExists(manifestPath))) {
const { manifest } = await buildManifest(appPath);
return manifest;
}
return await fs.readJson(manifestPath);
};
@@ -46,7 +46,6 @@ export default defineLogicFunction({
// databaseEventTriggerSettings: {
// eventName: 'objectName.created',
// },
],
});
`;
};