[REFACTOR] Workspace version only x.y.z (#10910)
# Introduction We want the APP_VERSION to be able to contains pre-release options, in a nutshell to be semVer compatible. But we want to have workspace, at least for the moment, that only store `x.y.z` and not `vx.y.z` or `x.y.z-alpha` version in database Explaining this refactor Related https://github.com/twentyhq/twenty/pull/10907
This commit is contained in:
@ -2,7 +2,7 @@
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when APP_VERSION is not defined 1`] = `[Error: Cannot run upgrade command when APP_VERSION is not defined, please double check your env variables]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when workspace version is not defined 1`] = `[Error: WORKSPACE_VERSION_NOT_DEFINED to=2.0.0]`;
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when workspace version is not defined 1`] = `[Error: WORKSPACE_VERSION_NOT_DEFINED workspace=workspace_0]`;
|
||||
|
||||
exports[`UpgradeCommandRunner Workspace upgrade should fail when workspace version is not equal to fromVersion 1`] = `[Error: WORKSPACE_VERSION_MISSMATCH Upgrade for workspace workspace_0 failed as its version is beneath fromWorkspaceVersion=2.0.0]`;
|
||||
|
||||
@ -10,4 +10,4 @@ exports[`UpgradeCommandRunner should run upgrade command with failing and succes
|
||||
|
||||
exports[`UpgradeCommandRunner should run upgrade command with failing and successful workspaces 2`] = `[Error: Received invalid version: invalid 1.0.0]`;
|
||||
|
||||
exports[`UpgradeCommandRunner should run upgrade command with failing and successful workspaces 3`] = `[Error: WORKSPACE_VERSION_NOT_DEFINED to=2.0.0]`;
|
||||
exports[`UpgradeCommandRunner should run upgrade command with failing and successful workspaces 3`] = `[Error: WORKSPACE_VERSION_NOT_DEFINED workspace=null_version_workspace]`;
|
||||
|
||||
@ -248,7 +248,8 @@ describe('UpgradeCommandRunner', () => {
|
||||
nullVersionWorkspace,
|
||||
];
|
||||
const totalWorkspace = numberOfValidWorkspace + failingWorkspaces.length;
|
||||
const appVersion = '2.0.0';
|
||||
const appVersion = 'v2.0.0';
|
||||
const expectedToVersion = '2.0.0';
|
||||
|
||||
await buildModuleAndSetupSpies({
|
||||
numberOfWorkspace: numberOfValidWorkspace,
|
||||
@ -280,7 +281,7 @@ describe('UpgradeCommandRunner', () => {
|
||||
expect(workspaceRepository.update).toHaveBeenNthCalledWith(
|
||||
numberOfValidWorkspace,
|
||||
{ id: expect.any(String) },
|
||||
{ version: appVersion },
|
||||
{ version: expectedToVersion },
|
||||
);
|
||||
|
||||
// Failing assertions
|
||||
|
||||
@ -17,11 +17,7 @@ import {
|
||||
CompareVersionMajorAndMinorReturnType,
|
||||
compareVersionMajorAndMinor,
|
||||
} from 'src/utils/version/compare-version-minor-and-major';
|
||||
|
||||
type ValidateWorkspaceVersionEqualsWorkspaceFromVersionOrThrowArgs = {
|
||||
workspaceId: string;
|
||||
appVersion: string | undefined;
|
||||
};
|
||||
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
|
||||
|
||||
export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMigrationCommandRunner {
|
||||
abstract readonly fromWorkspaceVersion: SemVer;
|
||||
@ -39,19 +35,18 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
|
||||
override async runOnWorkspace(args: RunOnWorkspaceArgs): Promise<void> {
|
||||
const { workspaceId, index, total, options } = args;
|
||||
const appVersion = this.environmentService.get('APP_VERSION');
|
||||
|
||||
this.logger.log(
|
||||
chalk.blue(
|
||||
`${options.dryRun ? '(dry run)' : ''} Upgrading workspace ${workspaceId} ${index + 1}/${total}`,
|
||||
),
|
||||
);
|
||||
const toVersion = this.retrieveToVersionFromAppVersion();
|
||||
|
||||
const workspaceVersionCompareResult =
|
||||
await this.retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion({
|
||||
appVersion,
|
||||
await this.retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion(
|
||||
workspaceId,
|
||||
});
|
||||
);
|
||||
|
||||
switch (workspaceVersionCompareResult) {
|
||||
case 'lower': {
|
||||
@ -66,7 +61,7 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
|
||||
await this.workspaceRepository.update(
|
||||
{ id: workspaceId },
|
||||
{ version: appVersion },
|
||||
{ version: toVersion },
|
||||
);
|
||||
this.logger.log(
|
||||
chalk.blue(`Upgrade for workspace ${workspaceId} completed.`),
|
||||
@ -91,16 +86,29 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
}
|
||||
}
|
||||
|
||||
private async retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion({
|
||||
appVersion,
|
||||
workspaceId,
|
||||
}: ValidateWorkspaceVersionEqualsWorkspaceFromVersionOrThrowArgs): Promise<CompareVersionMajorAndMinorReturnType> {
|
||||
private retrieveToVersionFromAppVersion() {
|
||||
const appVersion = this.environmentService.get('APP_VERSION');
|
||||
|
||||
if (!isDefined(appVersion)) {
|
||||
throw new Error(
|
||||
'Cannot run upgrade command when APP_VERSION is not defined, please double check your env variables',
|
||||
);
|
||||
}
|
||||
|
||||
const parsedVersion = extractVersionMajorMinorPatch(appVersion);
|
||||
|
||||
if (!isDefined(parsedVersion)) {
|
||||
throw new Error(
|
||||
`Should never occur, APP_VERSION is invalid ${parsedVersion}`,
|
||||
);
|
||||
}
|
||||
|
||||
return parsedVersion;
|
||||
}
|
||||
|
||||
private async retrieveWorkspaceVersionAndCompareToWorkspaceFromVersion(
|
||||
workspaceId: string,
|
||||
): Promise<CompareVersionMajorAndMinorReturnType> {
|
||||
// TODO remove after first release has been done using workspace_version
|
||||
if (!isDefined(this.VALIDATE_WORKSPACE_VERSION_FEATURE_FLAG)) {
|
||||
this.logger.warn(
|
||||
@ -116,7 +124,7 @@ export abstract class UpgradeCommandRunner extends ActiveOrSuspendedWorkspacesMi
|
||||
const currentWorkspaceVersion = workspace.version;
|
||||
|
||||
if (!isDefined(currentWorkspaceVersion)) {
|
||||
throw new Error(`WORKSPACE_VERSION_NOT_DEFINED to=${appVersion}`);
|
||||
throw new Error(`WORKSPACE_VERSION_NOT_DEFINED workspace=${workspaceId}`);
|
||||
}
|
||||
|
||||
return compareVersionMajorAndMinor(
|
||||
|
||||
@ -43,6 +43,7 @@ import { PermissionsService } from 'src/engine/metadata-modules/permissions/perm
|
||||
import { WorkspaceCacheStorageService } from 'src/engine/workspace-cache-storage/workspace-cache-storage.service';
|
||||
import { WorkspaceManagerService } from 'src/engine/workspace-manager/workspace-manager.service';
|
||||
import { DEFAULT_FEATURE_FLAGS } from 'src/engine/workspace-manager/workspace-sync-metadata/constants/default-feature-flags';
|
||||
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
|
||||
|
||||
@Injectable()
|
||||
// eslint-disable-next-line @nx/workspace-inject-workspace-repository
|
||||
@ -271,12 +272,12 @@ export class WorkspaceService extends TypeOrmQueryService<Workspace> {
|
||||
});
|
||||
await this.userWorkspaceService.createWorkspaceMember(workspace.id, user);
|
||||
|
||||
const appVersion = this.environmentService.get('APP_VERSION') ?? null;
|
||||
const appVersion = this.environmentService.get('APP_VERSION');
|
||||
|
||||
await this.workspaceRepository.update(workspace.id, {
|
||||
displayName: data.displayName,
|
||||
activationStatus: WorkspaceActivationStatus.ACTIVE,
|
||||
version: appVersion,
|
||||
version: extractVersionMajorMinorPatch(appVersion),
|
||||
});
|
||||
|
||||
return await this.workspaceRepository.findOneBy({
|
||||
|
||||
@ -0,0 +1,107 @@
|
||||
import { EachTestingContext } from 'twenty-shared';
|
||||
|
||||
import { extractVersionMajorMinorPatch } from 'src/utils/version/extract-version-major-minor-patch';
|
||||
|
||||
type IsSameVersionTestCase = EachTestingContext<{
|
||||
version: string | undefined;
|
||||
expected: string | null;
|
||||
}>;
|
||||
describe('extract-version-major-minor-patch', () => {
|
||||
const testCase: IsSameVersionTestCase[] = [
|
||||
{
|
||||
context: {
|
||||
version: '1.0.0',
|
||||
expected: '1.0.0',
|
||||
},
|
||||
title: 'Basic version',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '2.3.4',
|
||||
expected: '2.3.4',
|
||||
},
|
||||
title: 'Version with non-zero patch',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '0.1.0',
|
||||
expected: '0.1.0',
|
||||
},
|
||||
title: 'Version with zero major',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '1.0.0-alpha',
|
||||
expected: '1.0.0',
|
||||
},
|
||||
title: 'Version with pre-release tag',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '1.0.0-beta.1',
|
||||
expected: '1.0.0',
|
||||
},
|
||||
title: 'Version with pre-release tag and number',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: 'v1.0.0',
|
||||
expected: '1.0.0',
|
||||
},
|
||||
title: 'Version with v prefix',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '42.42.42',
|
||||
expected: '42.42.42',
|
||||
},
|
||||
title: 'Version with large numbers',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '1.2',
|
||||
expected: null,
|
||||
},
|
||||
title: 'Invalid version - missing patch number',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: 'invalid',
|
||||
expected: null,
|
||||
},
|
||||
title: 'Invalid version - not semver format',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: undefined,
|
||||
expected: null,
|
||||
},
|
||||
title: 'With undefined version',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '1.0.0.0',
|
||||
expected: null,
|
||||
},
|
||||
title: 'Invalid version - too many segments',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '1.a.0',
|
||||
expected: null,
|
||||
},
|
||||
title: 'Invalid version - non-numeric minor',
|
||||
},
|
||||
{
|
||||
context: {
|
||||
version: '',
|
||||
expected: null,
|
||||
},
|
||||
title: 'Invalid version - empty string',
|
||||
},
|
||||
];
|
||||
|
||||
test.each(testCase)('$title', ({ context: { version, expected } }) => {
|
||||
expect(extractVersionMajorMinorPatch(version)).toBe(expected);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,11 @@
|
||||
import semver from 'semver';
|
||||
|
||||
export const extractVersionMajorMinorPatch = (version: string | undefined) => {
|
||||
const parsed = semver.parse(version);
|
||||
|
||||
if (parsed === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
|
||||
};
|
||||
Reference in New Issue
Block a user