Refactor upgrade commands (#10592)

Simplifying a lot the upgrade system.

New way to upgrade:
`yarn command:prod upgrade`

New way to write upgrade commands (all wrapping is done for you)
```
  override async runOnWorkspace({
    index,
    total,
    workspaceId,
    options,
  }: RunOnWorkspaceArgs): Promise<void> {}
```

Also cleaning CommandModule imports to make it lighter
This commit is contained in:
Charles Bochet
2025-02-28 19:51:32 +01:00
committed by GitHub
parent 194b5889fe
commit baa3043954
44 changed files with 714 additions and 2212 deletions

View File

@ -1,35 +1,28 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Command, Option } from 'nest-commander';
import { Command } from 'nest-commander';
import { Repository } from 'typeorm';
import {
MaintainedWorkspacesMigrationCommandOptions,
MaintainedWorkspacesMigrationCommandRunner,
} from 'src/database/commands/migration-command/maintained-workspaces-migration-command.runner';
ActiveOrSuspendedWorkspacesMigrationCommandRunner,
RunOnWorkspaceArgs,
} from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceService } from 'src/engine/metadata-modules/data-source/data-source.service';
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
import { WorkspaceHealthService } from 'src/engine/workspace-manager/workspace-health/workspace-health.service';
import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/workspace-sync-metadata/workspace-sync-metadata.service';
import { SyncWorkspaceLoggerService } from './services/sync-workspace-logger.service';
interface RunWorkspaceMigrationsOptions
extends MaintainedWorkspacesMigrationCommandOptions {
force?: boolean;
}
@Command({
name: 'workspace:sync-metadata',
description: 'Sync metadata',
})
export class SyncWorkspaceMetadataCommand extends MaintainedWorkspacesMigrationCommandRunner {
export class SyncWorkspaceMetadataCommand extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
constructor(
@InjectRepository(Workspace, 'core')
protected readonly workspaceRepository: Repository<Workspace>,
private readonly workspaceSyncMetadataService: WorkspaceSyncMetadataService,
private readonly workspaceHealthService: WorkspaceHealthService,
private readonly dataSourceService: DataSourceService,
private readonly syncWorkspaceLoggerService: SyncWorkspaceLoggerService,
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
@ -37,109 +30,40 @@ export class SyncWorkspaceMetadataCommand extends MaintainedWorkspacesMigrationC
super(workspaceRepository, twentyORMGlobalManager);
}
async runMigrationCommandOnMaintainedWorkspaces(
_passedParam: string[],
options: RunWorkspaceMigrationsOptions,
workspaceIds: string[],
): Promise<void> {
this.logger.log(`Attempting to sync ${workspaceIds.length} workspaces.`);
override async runOnWorkspace({
workspaceId,
options,
index,
total,
}: RunOnWorkspaceArgs): Promise<void> {
this.logger.log(
`Running workspace sync for workspace: ${workspaceId} (${index} out of ${total})`,
);
let count = 1;
const errorsDuringSync: string[] = [];
for (const workspaceId of workspaceIds) {
this.logger.log(
`Running workspace sync for workspace: ${workspaceId} (${count} out of ${workspaceIds.length})`,
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId,
);
count++;
if (!options.force) {
try {
const issues =
await this.workspaceHealthService.healthCheck(workspaceId);
const { storage, workspaceMigrations } =
await this.workspaceSyncMetadataService.synchronize(
{
workspaceId,
dataSourceId: dataSourceMetadata.id,
},
{ applyChanges: !options.dryRun },
);
// Security: abort if there are issues.
if (issues.length > 0) {
if (!options.force) {
this.logger.error(
`Workspace contains ${issues.length} issues, aborting.`,
);
this.logger.log(
'If you want to force the migration, use --force flag',
);
this.logger.log(
'Please use `workspace:health` command to check issues and fix them before running this command.',
);
continue;
}
this.logger.warn(
`Workspace contains ${issues.length} issues, sync has been forced.`,
);
}
} catch (error) {
if (!options.force) {
throw error;
}
this.logger.warn(
`Workspace health check failed with error, but sync has been forced.`,
error,
);
}
}
try {
const dataSourceMetadata =
await this.dataSourceService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId,
);
const { storage, workspaceMigrations } =
await this.workspaceSyncMetadataService.synchronize(
{
workspaceId,
dataSourceId: dataSourceMetadata.id,
},
{ applyChanges: !options.dryRun },
);
if (options.dryRun) {
await this.syncWorkspaceLoggerService.saveLogs(
workspaceId,
storage,
workspaceMigrations,
);
}
} catch (error) {
errorsDuringSync.push(
`Failed to synchronize workspace ${workspaceId}: ${error.message}`,
);
continue;
}
if (options.dryRun) {
await this.syncWorkspaceLoggerService.saveLogs(
workspaceId,
storage,
workspaceMigrations,
);
}
this.logger.log(
`Finished synchronizing all active workspaces (${
workspaceIds.length
} workspaces). ${
errorsDuringSync.length > 0
? 'Errors during sync:\n' + errorsDuringSync.join('.\n')
: ''
}`,
`Finished synchronizing all active workspaces (${total} workspaces).`,
);
}
@Option({
flags: '-f, --force',
description: 'Force migration',
required: false,
})
force(): boolean {
return true;
}
}

View File

@ -3,6 +3,8 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { FeatureFlag } from 'src/engine/core-modules/feature-flag/feature-flag.entity';
import { FeatureFlagModule } from 'src/engine/core-modules/feature-flag/feature-flag.module';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { DataSourceModule } from 'src/engine/metadata-modules/data-source/data-source.module';
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
import { RelationMetadataEntity } from 'src/engine/metadata-modules/relation-metadata/relation-metadata.entity';
@ -10,6 +12,8 @@ import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/work
import { WorkspaceMigrationEntity } from 'src/engine/metadata-modules/workspace-migration/workspace-migration.entity';
import { WorkspaceMigrationBuilderModule } from 'src/engine/workspace-manager/workspace-migration-builder/workspace-migration-builder.module';
import { WorkspaceMigrationRunnerModule } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.module';
import { SyncWorkspaceLoggerService } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/services/sync-workspace-logger.service';
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
import { workspaceSyncMetadataComparators } from 'src/engine/workspace-manager/workspace-sync-metadata/comparators';
import { workspaceSyncMetadataFactories } from 'src/engine/workspace-manager/workspace-sync-metadata/factories';
import { WorkspaceMetadataUpdaterService } from 'src/engine/workspace-manager/workspace-sync-metadata/services/workspace-metadata-updater.service';
@ -34,7 +38,8 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
],
'metadata',
),
TypeOrmModule.forFeature([FeatureFlag], 'core'),
DataSourceModule,
TypeOrmModule.forFeature([Workspace, FeatureFlag], 'core'),
WorkspaceMetadataVersionModule,
],
providers: [
@ -47,7 +52,13 @@ import { WorkspaceSyncMetadataService } from 'src/engine/workspace-manager/works
WorkspaceSyncFieldMetadataService,
WorkspaceSyncMetadataService,
WorkspaceSyncIndexMetadataService,
SyncWorkspaceLoggerService,
SyncWorkspaceMetadataCommand,
],
exports: [
...workspaceSyncMetadataFactories,
WorkspaceSyncMetadataService,
SyncWorkspaceMetadataCommand,
],
exports: [...workspaceSyncMetadataFactories, WorkspaceSyncMetadataService],
})
export class WorkspaceSyncMetadataModule {}