13058 workflow with code fail to run (#13118)

- update publishOneServerlessFunction so it does not break the workflow
if failing
- update upgrade documentation to update `docker-compose.yml` when
mutated

Fixes https://github.com/twentyhq/twenty/issues/13058
This commit is contained in:
martmull
2025-07-09 14:48:44 +02:00
committed by GitHub
parent e84521e7b8
commit 156cb1b52f
5 changed files with 41 additions and 36 deletions

View File

@ -163,7 +163,7 @@ export class ServerlessFunctionResolver {
try { try {
const { id } = input; const { id } = input;
return await this.serverlessFunctionService.publishOneServerlessFunction( return await this.serverlessFunctionService.publishOneServerlessFunctionOrFail(
id, id,
workspaceId, workspaceId,
); );

View File

@ -30,6 +30,10 @@ import {
ServerlessFunctionException, ServerlessFunctionException,
ServerlessFunctionExceptionCode, ServerlessFunctionExceptionCode,
} from 'src/engine/metadata-modules/serverless-function/serverless-function.exception'; } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import {
WorkflowVersionStepException,
WorkflowVersionStepExceptionCode,
} from 'src/modules/workflow/common/exceptions/workflow-version-step.exception';
@Injectable() @Injectable()
export class ServerlessFunctionService { export class ServerlessFunctionService {
@ -137,7 +141,7 @@ export class ServerlessFunctionService {
return resultServerlessFunction; return resultServerlessFunction;
} }
async publishOneServerlessFunction(id: string, workspaceId: string) { async publishOneServerlessFunctionOrFail(id: string, workspaceId: string) {
const existingServerlessFunction = const existingServerlessFunction =
await this.serverlessFunctionRepository.findOneOrFail({ await this.serverlessFunctionRepository.findOneOrFail({
where: { where: {
@ -159,10 +163,7 @@ export class ServerlessFunctionService {
); );
if (deepEqual(latestCode, draftCode)) { if (deepEqual(latestCode, draftCode)) {
throw new ServerlessFunctionException( return existingServerlessFunction;
'Cannot publish a new version when code has not changed',
ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED,
);
} }
} }
@ -174,6 +175,7 @@ export class ServerlessFunctionService {
serverlessFunction: existingServerlessFunction, serverlessFunction: existingServerlessFunction,
version: 'draft', version: 'draft',
}); });
const newFolderPath = getServerlessFolder({ const newFolderPath = getServerlessFolder({
serverlessFunction: existingServerlessFunction, serverlessFunction: existingServerlessFunction,
version: newVersion, version: newVersion,
@ -197,9 +199,26 @@ export class ServerlessFunctionService {
}, },
); );
return this.serverlessFunctionRepository.findOneBy({ const publishedServerlessFunction =
id: existingServerlessFunction.id, await this.serverlessFunctionRepository.findOneOrFail({
}); where: {
id,
workspaceId,
},
});
// This check should never be thrown, but we encounter some issue with
// publishing serverless function in self hosted instances
// See https://github.com/twentyhq/twenty/issues/13058
// TODO: remove this check when issue solved
if (!isDefined(publishedServerlessFunction.latestVersion)) {
throw new WorkflowVersionStepException(
`Fail to publish serverlessFunction ${publishedServerlessFunction.id}.Received latest version ${publishedServerlessFunction.latestVersion}`,
WorkflowVersionStepExceptionCode.FAILURE,
);
}
return publishedServerlessFunction;
} }
async deleteOneServerlessFunction({ async deleteOneServerlessFunction({

View File

@ -50,7 +50,7 @@ describe('WorkflowStatusesUpdate', () => {
}; };
const mockServerlessFunctionService = { const mockServerlessFunctionService = {
publishOneServerlessFunction: jest.fn(), publishOneServerlessFunctionOrFail: jest.fn(),
findOneOrFail: jest.fn(), findOneOrFail: jest.fn(),
}; };
@ -256,6 +256,9 @@ describe('WorkflowStatusesUpdate', () => {
mockServerlessFunctionService.findOneOrFail.mockResolvedValue( mockServerlessFunctionService.findOneOrFail.mockResolvedValue(
mockServerlessFunction, mockServerlessFunction,
); );
mockServerlessFunctionService.publishOneServerlessFunctionOrFail.mockResolvedValue(
mockServerlessFunction,
);
await job.handle(event); await job.handle(event);
@ -264,7 +267,7 @@ describe('WorkflowStatusesUpdate', () => {
mockWorkflowVersionRepository.findOneOrFail, mockWorkflowVersionRepository.findOneOrFail,
).toHaveBeenCalledTimes(1); ).toHaveBeenCalledTimes(1);
expect( expect(
mockServerlessFunctionService.publishOneServerlessFunction, mockServerlessFunctionService.publishOneServerlessFunctionOrFail,
).toHaveBeenCalledWith('serverless-1', '1'); ).toHaveBeenCalledWith('serverless-1', '1');
expect(mockWorkflowVersionRepository.update).toHaveBeenCalledWith('1', { expect(mockWorkflowVersionRepository.update).toHaveBeenCalledWith('1', {
steps: [ steps: [

View File

@ -11,7 +11,6 @@ import { Processor } from 'src/engine/core-modules/message-queue/decorators/proc
import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants'; import { MessageQueue } from 'src/engine/core-modules/message-queue/message-queue.constants';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity'; import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity'; import { ServerlessFunctionEntity } from 'src/engine/metadata-modules/serverless-function/serverless-function.entity';
import { ServerlessFunctionExceptionCode } from 'src/engine/metadata-modules/serverless-function/serverless-function.exception';
import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service'; import { ServerlessFunctionService } from 'src/engine/metadata-modules/serverless-function/serverless-function.service';
import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository'; import { WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager'; import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
@ -198,33 +197,11 @@ export class WorkflowStatusesUpdateJob {
const newStep = { ...step }; const newStep = { ...step };
if (step.type === WorkflowActionType.CODE) { if (step.type === WorkflowActionType.CODE) {
try { const serverlessFunction =
await this.serverlessFunctionService.publishOneServerlessFunction( await this.serverlessFunctionService.publishOneServerlessFunctionOrFail(
step.settings.input.serverlessFunctionId, step.settings.input.serverlessFunctionId,
workspaceId, workspaceId,
); );
} catch (e) {
// publishOneServerlessFunction throws if no change have been
// applied between draft and lastPublished version.
// If no change have been applied, we just use the same
// serverless function version
if (
e.code !==
ServerlessFunctionExceptionCode.SERVERLESS_FUNCTION_CODE_UNCHANGED
) {
this.logger.error(
`Error while publishing serverless function '${step.settings.input.serverlessFunctionId}': ${e}`,
);
}
}
const serverlessFunction =
await this.serverlessFunctionRepository.findOneOrFail({
where: {
id: step.settings.input.serverlessFunctionId,
workspaceId,
},
});
const newStepSettings = { ...step.settings }; const newStepSettings = { ...step.settings };
@ -233,6 +210,7 @@ export class WorkflowStatusesUpdateJob {
newStep.settings = newStepSettings; newStep.settings = newStepSettings;
} }
newSteps.push(newStep); newSteps.push(newStep);
} }

View File

@ -119,6 +119,11 @@ yarn database:migrate:prod
yarn command:prod upgrade yarn command:prod upgrade
``` ```
#### Docker-compose.yml mutation
This version includes a `docker-compose.yml` mutation to give `worker` service access to the `server-local-data` volume.
Please update your local `docker-compose.yml` with [v0.50.0 docker-compose.yml](https://github.com/twentyhq/twenty/blob/v0.50.0/packages/twenty-docker/docker-compose.yml)
### v0.43.0 to v0.44.0 ### v0.43.0 to v0.44.0
Upgrade your Twenty instance to use v0.44.0 image Upgrade your Twenty instance to use v0.44.0 image