Improve upgrade command and prepare 1.5 release (#14325)
In this PR: - refactor the upgrade command / upgrade command runner to keep upgrade command as light as possible (all wrapping logic should go to upgrade command runner) - prevent any upgrade if there is at least one workspace.version < previsousVersion ==> this leads to corrupted state where only core migrations are run if the self-hoster is skipping a version
This commit is contained in:
+17
-125
@@ -1,13 +1,7 @@
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { exec } from 'child_process';
|
||||
import { promisify } from 'util';
|
||||
|
||||
import chalk from 'chalk';
|
||||
import { Command } from 'nest-commander';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { In, Repository } from 'typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { type ActiveOrSuspendedWorkspacesMigrationCommandOptions } from 'src/database/commands/command-runners/active-or-suspended-workspaces-migration.command-runner';
|
||||
import {
|
||||
@@ -31,96 +25,13 @@ import { RemoveWorkflowRunsWithoutState } from 'src/database/commands/upgrade-ve
|
||||
import { AddNextStepIdsToWorkflowRunsTrigger } from 'src/database/commands/upgrade-version-command/1-3/1-3-add-next-step-ids-to-workflow-runs-trigger.command';
|
||||
import { AssignRolesToExistingApiKeysCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-assign-roles-to-existing-api-keys.command';
|
||||
import { UpdateTimestampColumnTypeInWorkspaceSchemaCommand } from 'src/database/commands/upgrade-version-command/1-3/1-3-update-timestamp-column-type-in-workspace-schema.command';
|
||||
import { AddPositionsToWorkflowVersionsAndWorkflowRuns } from 'src/database/commands/upgrade-version-command/1-5/1-5-add-positions-to-workflow-versions-and-workflow-runs.command';
|
||||
import { RemoveFavoriteViewRelation } from 'src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command';
|
||||
import { AddPositionsToWorkflowVersionsAndWorkflowRunsCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-add-positions-to-workflow-versions-and-workflow-runs.command';
|
||||
import { MigrateViewsToCoreCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-migrate-views-to-core.command';
|
||||
import { RemoveFavoriteViewRelationCommand } from 'src/database/commands/upgrade-version-command/1-5/1-5-remove-favorite-view-relation.command';
|
||||
import { TwentyConfigService } from 'src/engine/core-modules/twenty-config/twenty-config.service';
|
||||
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.manager';
|
||||
import { SyncWorkspaceMetadataCommand } from 'src/engine/workspace-manager/workspace-sync-metadata/commands/sync-workspace-metadata.command';
|
||||
import { compareVersionMajorAndMinor } from 'src/utils/version/compare-version-minor-and-major';
|
||||
|
||||
const execPromise = promisify(exec);
|
||||
|
||||
@Injectable()
|
||||
export class DatabaseMigrationService {
|
||||
private logger = new Logger(DatabaseMigrationService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Workspace)
|
||||
private readonly workspaceRepository: Repository<Workspace>,
|
||||
) {}
|
||||
|
||||
// TODO centralize with ActiveOrSuspendedRunner method
|
||||
private async loadActiveOrSuspendedWorkspace() {
|
||||
return await this.workspaceRepository.find({
|
||||
select: ['id', 'version'],
|
||||
where: {
|
||||
activationStatus: In([
|
||||
WorkspaceActivationStatus.ACTIVE,
|
||||
WorkspaceActivationStatus.SUSPENDED,
|
||||
]),
|
||||
},
|
||||
order: {
|
||||
id: 'ASC',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async shouldSkipUpgradeIfFreshInstallation(): Promise<boolean> {
|
||||
const activeWorkspaceOrSuspendedWorkspaceCount =
|
||||
await this.loadActiveOrSuspendedWorkspace();
|
||||
|
||||
return activeWorkspaceOrSuspendedWorkspaceCount.length === 0;
|
||||
}
|
||||
|
||||
async runMigrations(): Promise<void> {
|
||||
this.logger.log('Running global database migrations');
|
||||
|
||||
try {
|
||||
this.logger.log('Running core datasource migrations...');
|
||||
const coreResult = await execPromise(
|
||||
'npx -y typeorm migration:run -d dist/src/database/typeorm/core/core.datasource',
|
||||
);
|
||||
|
||||
this.logger.log(coreResult.stdout);
|
||||
|
||||
this.logger.log('Database migrations completed successfully');
|
||||
} catch (error) {
|
||||
this.logger.error('Error running database migrations:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async areAllWorkspacesAboveVersion0_53(): Promise<boolean> {
|
||||
try {
|
||||
const allActiveOrSuspendedWorkspaces =
|
||||
await this.loadActiveOrSuspendedWorkspace();
|
||||
|
||||
if (allActiveOrSuspendedWorkspaces.length === 0) {
|
||||
this.logger.log(
|
||||
'No workspaces found. Running migrations for fresh installation.',
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const workspacesBelowVersion = allActiveOrSuspendedWorkspaces.filter(
|
||||
({ version }) =>
|
||||
version === null ||
|
||||
compareVersionMajorAndMinor(version, '0.53.0') === 'lower',
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Found ${workspacesBelowVersion.length} active or suspended workspaces that are below version 0.53.0 \n${workspacesBelowVersion.map((el) => el.id).join('\n')}`,
|
||||
);
|
||||
|
||||
return workspacesBelowVersion.length === 0;
|
||||
} catch (error) {
|
||||
this.logger.error('Error checking workspaces below version:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Command({
|
||||
name: 'upgrade',
|
||||
@@ -136,8 +47,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly twentyORMGlobalManager: TwentyORMGlobalManager,
|
||||
protected readonly syncWorkspaceMetadataCommand: SyncWorkspaceMetadataCommand,
|
||||
|
||||
private readonly databaseMigrationService: DatabaseMigrationService,
|
||||
|
||||
// 0.54 Commands
|
||||
protected readonly fixStandardSelectFieldsPositionCommand: FixStandardSelectFieldsPositionCommand,
|
||||
protected readonly fixCreatedByDefaultValueCommand: FixCreatedByDefaultValueCommand,
|
||||
@@ -165,8 +74,9 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
protected readonly updateTimestampColumnTypeInWorkspaceSchemaCommand: UpdateTimestampColumnTypeInWorkspaceSchemaCommand,
|
||||
|
||||
// 1.5 Commands
|
||||
protected readonly removeFavoriteViewRelation: RemoveFavoriteViewRelation,
|
||||
protected readonly addPositionsToWorkflowVersionsAndWorkflowRuns: AddPositionsToWorkflowVersionsAndWorkflowRuns,
|
||||
protected readonly removeFavoriteViewRelationCommand: RemoveFavoriteViewRelationCommand,
|
||||
protected readonly addPositionsToWorkflowVersionsAndWorkflowRunsCommand: AddPositionsToWorkflowVersionsAndWorkflowRunsCommand,
|
||||
protected readonly migrateViewsToCoreCommand: MigrateViewsToCoreCommand,
|
||||
) {
|
||||
super(
|
||||
workspaceRepository,
|
||||
@@ -242,12 +152,18 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
|
||||
const commands_150: VersionCommands = {
|
||||
beforeSyncMetadata: [
|
||||
this.removeFavoriteViewRelation,
|
||||
this.addPositionsToWorkflowVersionsAndWorkflowRuns,
|
||||
this.migrateViewsToCoreCommand,
|
||||
this.removeFavoriteViewRelationCommand,
|
||||
this.addPositionsToWorkflowVersionsAndWorkflowRunsCommand,
|
||||
],
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
const commands_160: VersionCommands = {
|
||||
beforeSyncMetadata: [],
|
||||
afterSyncMetadata: [],
|
||||
};
|
||||
|
||||
this.allCommands = {
|
||||
'0.53.0': commands_053,
|
||||
'0.54.0': commands_054,
|
||||
@@ -259,6 +175,7 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
'1.3.0': commands_130,
|
||||
'1.4.0': commands_140,
|
||||
'1.5.0': commands_150,
|
||||
'1.6.0': commands_160,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -266,31 +183,6 @@ export class UpgradeCommand extends UpgradeCommandRunner {
|
||||
passedParams: string[],
|
||||
options: ActiveOrSuspendedWorkspacesMigrationCommandOptions,
|
||||
): Promise<void> {
|
||||
const shouldSkipUpgradeIfFreshInstallation =
|
||||
await this.databaseMigrationService.shouldSkipUpgradeIfFreshInstallation();
|
||||
|
||||
if (shouldSkipUpgradeIfFreshInstallation) {
|
||||
this.logger.log(
|
||||
chalk.blue('Fresh installation detected, skipping migration'),
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldPreventFromUpgradingIfWorkspaceIsBelowVersion0_53 =
|
||||
!(await this.databaseMigrationService.areAllWorkspacesAboveVersion0_53());
|
||||
|
||||
if (shouldPreventFromUpgradingIfWorkspaceIsBelowVersion0_53) {
|
||||
this.logger.log(
|
||||
chalk.red(
|
||||
'Not able to run migrate command, aborting the whole migrate-upgrade operation',
|
||||
),
|
||||
);
|
||||
throw new Error('Could not run migration aborting');
|
||||
}
|
||||
|
||||
await this.databaseMigrationService.runMigrations();
|
||||
|
||||
await super.runMigrationCommand(passedParams, options);
|
||||
return await super.runMigrationCommand(passedParams, options);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user