Allow to stop running workflow (#15270)

https://github.com/user-attachments/assets/599154e4-8743-471b-b05a-721b635bcf4e

On stoppage:
- if no running steps, mark pending as failed and end the workflow
- if running steps, set as stopping and exit. Going to the next step, as
the workflow is not running anymore, it will naturally stop
This commit is contained in:
Thomas Trompette
2025-10-23 15:01:15 +02:00
committed by GitHub
parent 1cf442966d
commit 4effa5351c
23 changed files with 532 additions and 70 deletions
@@ -0,0 +1,124 @@
import { InjectDataSource, InjectRepository } from '@nestjs/typeorm';
import { Command } from 'nest-commander';
import { DataSource, Repository } from 'typeorm';
import {
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
type RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { getWorkspaceSchemaName } from 'src/engine/workspace-datasource/utils/get-workspace-schema-name.util';
import { WORKFLOW_RUN_STANDARD_FIELD_IDS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/standard-field-ids';
import { WorkflowRunStatus } from 'src/modules/workflow/common/standard-objects/workflow-run.workspace-entity';
@Command({
name: 'upgrade:1-10:add-workflow-run-stop-statuses',
description: 'Add stopped and stopping statuses to workflow runs',
})
export class AddWorkflowRunStopStatusesCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(WorkspaceEntity)
protected readonly workspaceRepository: Repository<WorkspaceEntity>,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@InjectRepository(FieldMetadataEntity)
private readonly fieldMetadataRepository: Repository<FieldMetadataEntity>,
@InjectDataSource()
private readonly coreDataSource: DataSource,
) {
super(workspaceRepository, twentyORMGlobalManager);
}
override async runOnWorkspace({
workspaceId,
options,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Adding stopped and stopping statuses to workflow runs for workspace ${workspaceId}`,
);
const workflowRunStatusFieldMetadata =
await this.fieldMetadataRepository.findOne({
where: {
standardId: WORKFLOW_RUN_STANDARD_FIELD_IDS.status,
},
});
if (!workflowRunStatusFieldMetadata) {
this.logger.error(
`Workflow run status field metadata not found for workspace ${workspaceId}`,
);
return;
}
const workflowRunStatusFieldMetadataOptions =
workflowRunStatusFieldMetadata.options;
if (
workflowRunStatusFieldMetadataOptions?.some(
(option) =>
option.value === WorkflowRunStatus.STOPPED ||
option.value === WorkflowRunStatus.STOPPING,
)
) {
this.logger.log(
`Workflow run status field metadata options already contain stopped and stopping statuses for workspace ${workspaceId}`,
);
return;
} else if (options.dryRun) {
this.logger.log(
`Would add stopped and stopping statuses to workflow run status field metadata for workspace ${workspaceId}`,
);
} else {
workflowRunStatusFieldMetadataOptions?.push({
value: WorkflowRunStatus.STOPPING,
label: 'Stopping',
position: 5,
color: 'orange',
});
workflowRunStatusFieldMetadataOptions?.push({
value: WorkflowRunStatus.STOPPED,
label: 'Stopped',
position: 6,
color: 'gray',
});
await this.fieldMetadataRepository.save(workflowRunStatusFieldMetadata);
this.logger.log(
`Stopped and stopping statuses added to workflow run status field metadata for workspace ${workspaceId}`,
);
}
const schemaName = getWorkspaceSchemaName(workspaceId);
if (options.dryRun) {
this.logger.log(
`Would try to add stopped and stopping statuses to workflow run status enum for workspace ${workspaceId}`,
);
} else {
try {
await this.coreDataSource.query(
`ALTER TYPE ${schemaName}."workflowRun_status_enum" ADD VALUE 'STOPPING'`,
);
this.logger.log(
`Stopping status added to workflow run status enum for workspace ${workspaceId}`,
);
await this.coreDataSource.query(
`ALTER TYPE ${schemaName}."workflowRun_status_enum" ADD VALUE 'STOPPED'`,
);
this.logger.log(
`Stopped status added to workflow run status enum for workspace ${workspaceId}`,
);
} catch (error) {
this.logger.error(
`Error adding stopped and stopping statuses to workflow run status enum for workspace ${workspaceId}: ${error}`,
);
}
}
}
}
@@ -1,23 +1,27 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AddWorkflowRunStopStatusesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-add-workflow-run-stop-statuses.command';
import { MigrateAttachmentAuthorToCreatedByCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-author-to-created-by.command';
import { MigrateAttachmentTypeToFileCategoryCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-type-to-file-category.command';
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { WorkspaceSchemaManagerModule } from 'src/engine/twenty-orm/workspace-schema-manager/workspace-schema-manager.module';
@Module({
imports: [
TypeOrmModule.forFeature([WorkspaceEntity]),
TypeOrmModule.forFeature([WorkspaceEntity, FieldMetadataEntity]),
WorkspaceSchemaManagerModule,
],
providers: [
MigrateAttachmentAuthorToCreatedByCommand,
MigrateAttachmentTypeToFileCategoryCommand,
AddWorkflowRunStopStatusesCommand,
],
exports: [
MigrateAttachmentAuthorToCreatedByCommand,
MigrateAttachmentTypeToFileCategoryCommand,
AddWorkflowRunStopStatusesCommand,
],
})
export class V1_10_UpgradeVersionCommandModule {}
@@ -19,6 +19,7 @@ import { AddEnqueuedStatusToWorkflowRunCommand } from 'src/database/commands/upg
import { FixSchemaArrayTypeCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-schema-array-type.command';
import { FixUpdateStandardFieldsIsLabelSyncedWithName } from 'src/database/commands/upgrade-version-command/1-1/1-1-fix-update-standard-field-is-label-synced-with-name.command';
import { MigrateWorkflowRunStatesCommand } from 'src/database/commands/upgrade-version-command/1-1/1-1-migrate-workflow-run-state.command';
import { AddWorkflowRunStopStatusesCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-add-workflow-run-stop-statuses.command';
import { MigrateAttachmentAuthorToCreatedByCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-author-to-created-by.command';
import { MigrateAttachmentTypeToFileCategoryCommand } from 'src/database/commands/upgrade-version-command/1-10/1-10-migrate-attachment-type-to-file-category.command';
import { AddEnqueuedStatusToWorkflowRunV2Command } from 'src/database/commands/upgrade-version-command/1-2/1-2-add-enqueued-status-to-workflow-run-v2.command';
@@ -101,6 +102,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
// 1.10 Commands
protected readonly migrateAttachmentAuthorToCreatedByCommand: MigrateAttachmentAuthorToCreatedByCommand,
protected readonly migrateAttachmentTypeToFileCategoryCommand: MigrateAttachmentTypeToFileCategoryCommand,
protected readonly addWorkflowRunStopStatusesCommand: AddWorkflowRunStopStatusesCommand,
) {
super(
workspaceRepository,
@@ -206,7 +208,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
};
const commands_1100: VersionCommands = {
beforeSyncMetadata: [],
beforeSyncMetadata: [this.addWorkflowRunStopStatusesCommand],
afterSyncMetadata: [
this.migrateAttachmentAuthorToCreatedByCommand,
this.migrateAttachmentTypeToFileCategoryCommand,