@contember/cli 1.4.0-alpha.1 → 1.4.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +2 -2
- package/dist/src/commands/deploy/DeployCommand.d.ts +1 -1
- package/dist/src/commands/deploy/DeployCommand.d.ts.map +1 -1
- package/dist/src/commands/deploy/DeployCommand.js +8 -3
- package/dist/src/commands/deploy/DeployCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationAmendCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationAmendCommand.js +6 -6
- package/dist/src/commands/migrations/MigrationAmendCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationBlankCommand.d.ts +15 -0
- package/dist/src/commands/migrations/MigrationBlankCommand.d.ts.map +1 -0
- package/dist/src/commands/migrations/MigrationBlankCommand.js +29 -0
- package/dist/src/commands/migrations/MigrationBlankCommand.js.map +1 -0
- package/dist/src/commands/migrations/MigrationDescribeCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationDescribeCommand.js +5 -5
- package/dist/src/commands/migrations/MigrationDescribeCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.js +8 -3
- package/dist/src/commands/migrations/MigrationDiffCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationExecuteCommand.d.ts +1 -1
- package/dist/src/commands/migrations/MigrationExecuteCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationExecuteCommand.js +8 -3
- package/dist/src/commands/migrations/MigrationExecuteCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationRebaseCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationRebaseCommand.js +2 -3
- package/dist/src/commands/migrations/MigrationRebaseCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationStatusCommand.d.ts +1 -2
- package/dist/src/commands/migrations/MigrationStatusCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationStatusCommand.js +5 -7
- package/dist/src/commands/migrations/MigrationStatusCommand.js.map +1 -1
- package/dist/src/commands/migrations/index.d.ts +1 -0
- package/dist/src/commands/migrations/index.d.ts.map +1 -1
- package/dist/src/commands/migrations/index.js +1 -0
- package/dist/src/commands/migrations/index.js.map +1 -1
- package/dist/src/commands/project/ProjectValidateCommand.js +2 -2
- package/dist/src/commands/project/ProjectValidateCommand.js.map +1 -1
- package/dist/src/run.js +1 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/admin.d.ts.map +1 -1
- package/dist/src/utils/admin.js +1 -5
- package/dist/src/utils/admin.js.map +1 -1
- package/dist/src/utils/esbuild.d.ts +2 -0
- package/dist/src/utils/esbuild.d.ts.map +1 -0
- package/dist/src/utils/esbuild.js +41 -0
- package/dist/src/utils/esbuild.js.map +1 -0
- package/dist/src/utils/migrations/JsLoader.d.ts +9 -0
- package/dist/src/utils/migrations/JsLoader.d.ts.map +1 -0
- package/dist/src/utils/migrations/JsLoader.js +33 -0
- package/dist/src/utils/migrations/JsLoader.js.map +1 -0
- package/dist/src/utils/migrations/JsonLoader.d.ts +9 -0
- package/dist/src/utils/migrations/JsonLoader.d.ts.map +1 -0
- package/dist/src/utils/migrations/JsonLoader.js +17 -0
- package/dist/src/utils/migrations/JsonLoader.js.map +1 -0
- package/dist/src/{commands → utils}/migrations/MigrationCreateHelper.d.ts +6 -3
- package/dist/src/utils/migrations/MigrationCreateHelper.d.ts.map +1 -0
- package/dist/src/{commands → utils}/migrations/MigrationCreateHelper.js +1 -1
- package/dist/src/utils/migrations/MigrationCreateHelper.js.map +1 -0
- package/dist/src/utils/migrations/MigrationCreator.d.ts +21 -0
- package/dist/src/utils/migrations/MigrationCreator.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationCreator.js +47 -0
- package/dist/src/utils/migrations/MigrationCreator.js.map +1 -0
- package/dist/src/{commands → utils}/migrations/MigrationExecuteHelper.d.ts +8 -4
- package/dist/src/utils/migrations/MigrationExecuteHelper.d.ts.map +1 -0
- package/dist/src/{commands → utils}/migrations/MigrationExecuteHelper.js +57 -5
- package/dist/src/utils/migrations/MigrationExecuteHelper.js.map +1 -0
- package/dist/src/utils/migrations/MigrationFile.d.ts +36 -0
- package/dist/src/utils/migrations/MigrationFile.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationFile.js +6 -0
- package/dist/src/utils/migrations/MigrationFile.js.map +1 -0
- package/dist/src/utils/migrations/MigrationFileLoader.d.ts +5 -0
- package/dist/src/utils/migrations/MigrationFileLoader.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationFileLoader.js +3 -0
- package/dist/src/utils/migrations/MigrationFileLoader.js.map +1 -0
- package/dist/src/utils/migrations/MigrationFilesManager.d.ts +18 -0
- package/dist/src/utils/migrations/MigrationFilesManager.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationFilesManager.js +114 -0
- package/dist/src/utils/migrations/MigrationFilesManager.js.map +1 -0
- package/dist/src/utils/migrations/MigrationParser.d.ts +6 -0
- package/dist/src/utils/migrations/MigrationParser.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationParser.js +33 -0
- package/dist/src/utils/migrations/MigrationParser.js.map +1 -0
- package/dist/src/{MigrationsContainer.d.ts → utils/migrations/MigrationsContainer.d.ts} +5 -1
- package/dist/src/utils/migrations/MigrationsContainer.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationsContainer.js +50 -0
- package/dist/src/utils/migrations/MigrationsContainer.js.map +1 -0
- package/dist/src/utils/migrations/MigrationsResolver.d.ts +14 -0
- package/dist/src/utils/migrations/MigrationsResolver.d.ts.map +1 -0
- package/dist/src/utils/migrations/MigrationsResolver.js +37 -0
- package/dist/src/utils/migrations/MigrationsResolver.js.map +1 -0
- package/dist/src/utils/migrations/SchemaVersionBuilder.d.ts +13 -0
- package/dist/src/utils/migrations/SchemaVersionBuilder.d.ts.map +1 -0
- package/dist/src/utils/migrations/SchemaVersionBuilder.js +26 -0
- package/dist/src/utils/migrations/SchemaVersionBuilder.js.map +1 -0
- package/dist/src/utils/{migrations.d.ts → migrations/migrations.d.ts} +18 -22
- package/dist/src/utils/migrations/migrations.d.ts.map +1 -0
- package/dist/src/utils/{migrations.js → migrations/migrations.js} +13 -28
- package/dist/src/utils/migrations/migrations.js.map +1 -0
- package/dist/src/utils/project/loadSchema.d.ts.map +1 -1
- package/dist/src/utils/project/loadSchema.js +5 -44
- package/dist/src/utils/project/loadSchema.js.map +1 -1
- package/dist/src/utils/project/resources/out.css +106 -124
- package/dist/src/utils/system/index.d.ts +3 -4
- package/dist/src/utils/system/index.d.ts.map +1 -1
- package/dist/src/utils/system/index.js.map +1 -1
- package/package.json +26 -27
- package/src/commands/deploy/DeployCommand.ts +9 -4
- package/src/commands/migrations/MigrationAmendCommand.ts +7 -11
- package/src/commands/migrations/MigrationBlankCommand.ts +46 -0
- package/src/commands/migrations/MigrationDescribeCommand.ts +7 -5
- package/src/commands/migrations/MigrationDiffCommand.ts +8 -3
- package/src/commands/migrations/MigrationExecuteCommand.ts +8 -3
- package/src/commands/migrations/MigrationRebaseCommand.ts +2 -3
- package/src/commands/migrations/MigrationStatusCommand.ts +6 -9
- package/src/commands/migrations/index.ts +1 -0
- package/src/commands/project/ProjectValidateCommand.ts +2 -2
- package/src/run.ts +2 -0
- package/src/tsconfig.json +1 -0
- package/src/utils/admin.ts +0 -1
- package/src/utils/esbuild.ts +14 -0
- package/src/utils/migrations/JsLoader.ts +37 -0
- package/src/utils/migrations/JsonLoader.ts +18 -0
- package/src/{commands → utils}/migrations/MigrationCreateHelper.ts +7 -11
- package/src/utils/migrations/MigrationCreator.ts +60 -0
- package/src/{commands → utils}/migrations/MigrationExecuteHelper.ts +68 -9
- package/src/utils/migrations/MigrationFile.ts +52 -0
- package/src/utils/migrations/MigrationFileLoader.ts +5 -0
- package/src/utils/migrations/MigrationFilesManager.ts +102 -0
- package/src/utils/migrations/MigrationParser.ts +32 -0
- package/src/{MigrationsContainer.ts → utils/migrations/MigrationsContainer.ts} +29 -12
- package/src/utils/migrations/MigrationsResolver.ts +44 -0
- package/src/utils/migrations/SchemaVersionBuilder.ts +33 -0
- package/src/utils/{migrations.ts → migrations/migrations.ts} +33 -57
- package/src/utils/project/loadSchema.ts +7 -21
- package/src/utils/system/index.ts +3 -4
- package/dist/src/MigrationsContainer.d.ts.map +0 -1
- package/dist/src/MigrationsContainer.js +0 -25
- package/dist/src/MigrationsContainer.js.map +0 -1
- package/dist/src/commands/migrations/MigrationCreateHelper.d.ts.map +0 -1
- package/dist/src/commands/migrations/MigrationCreateHelper.js.map +0 -1
- package/dist/src/commands/migrations/MigrationExecuteHelper.d.ts.map +0 -1
- package/dist/src/commands/migrations/MigrationExecuteHelper.js.map +0 -1
- package/dist/src/utils/migrations.d.ts.map +0 -1
- package/dist/src/utils/migrations.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
2
|
import { printValidationErrors } from '../../utils/schema'
|
|
3
3
|
import { InvalidSchemaException } from '@contember/schema-migrations'
|
|
4
|
-
import { executeCreateMigrationCommand } from '
|
|
5
|
-
import { createMigrationStatusTable, printMigrationDescription } from '../../utils/migrations'
|
|
6
|
-
import { executeMigrations, resolveMigrationStatus } from '
|
|
4
|
+
import { executeCreateMigrationCommand } from '../../utils/migrations/MigrationCreateHelper'
|
|
5
|
+
import { createMigrationStatusTable, printMigrationDescription } from '../../utils/migrations/migrations'
|
|
6
|
+
import { executeMigrations, resolveMigrationStatus } from '../../utils/migrations/MigrationExecuteHelper'
|
|
7
7
|
import prompts from 'prompts'
|
|
8
8
|
import { interactiveResolveApiToken, TenantClient } from '../../utils/tenant'
|
|
9
9
|
import { interactiveResolveInstanceEnvironmentFromInput } from '../../utils/instance'
|
|
@@ -110,6 +110,11 @@ export class MigrationDiffCommand extends Command<Args, Options> {
|
|
|
110
110
|
requireConfirmation: status.migrationsToExecute.length > 1 && !yes,
|
|
111
111
|
schemaVersionBuilder,
|
|
112
112
|
migrationDescriber,
|
|
113
|
+
contentMigrationFactoryArgs: {
|
|
114
|
+
apiToken,
|
|
115
|
+
apiBaseUrl: instance.baseUrl.replace(/\/$/, ''),
|
|
116
|
+
projectName: project.name,
|
|
117
|
+
},
|
|
113
118
|
})
|
|
114
119
|
}
|
|
115
120
|
return 0
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
|
-
import { MigrationsContainerFactory } from '../../MigrationsContainer'
|
|
2
|
+
import { MigrationsContainerFactory } from '../../utils/migrations/MigrationsContainer'
|
|
3
3
|
import {
|
|
4
4
|
configureExecuteMigrationCommand,
|
|
5
5
|
ExecuteMigrationOptions,
|
|
6
6
|
executeMigrations,
|
|
7
7
|
resolveMigrationStatus,
|
|
8
|
-
} from '
|
|
9
|
-
import { createMigrationStatusTable } from '../../utils/migrations'
|
|
8
|
+
} from '../../utils/migrations/MigrationExecuteHelper'
|
|
9
|
+
import { createMigrationStatusTable } from '../../utils/migrations/migrations'
|
|
10
10
|
import { interactiveResolveInstanceEnvironmentFromInput } from '../../utils/instance'
|
|
11
11
|
import { interactiveResolveApiToken, TenantClient } from '../../utils/tenant'
|
|
12
12
|
import { SystemClient } from '../../utils/system'
|
|
@@ -90,6 +90,11 @@ export class MigrationExecuteCommand extends Command<Args, Options> {
|
|
|
90
90
|
migrations,
|
|
91
91
|
requireConfirmation: !input.getOption('yes'),
|
|
92
92
|
force: force,
|
|
93
|
+
contentMigrationFactoryArgs: {
|
|
94
|
+
apiToken,
|
|
95
|
+
apiBaseUrl: instance.baseUrl.replace(/\/$/, ''),
|
|
96
|
+
projectName: remoteProject,
|
|
97
|
+
},
|
|
93
98
|
})
|
|
94
99
|
code ||= singleCode
|
|
95
100
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
2
|
import { MigrationVersionHelper } from '@contember/schema-migrations'
|
|
3
|
-
import { executeCreateMigrationCommand } from '
|
|
4
|
-
import { getMigrationByName } from '../../utils/migrations'
|
|
3
|
+
import { executeCreateMigrationCommand } from '../../utils/migrations/MigrationCreateHelper'
|
|
5
4
|
import { resolveSystemApiClient } from './SystemApiClientResolver'
|
|
6
5
|
import { emptySchema } from '@contember/schema-utils'
|
|
7
6
|
import { validateMigrations } from './MigrationValidationHelper'
|
|
@@ -52,7 +51,7 @@ export class MigrationRebaseCommand extends Command<Args, Options> {
|
|
|
52
51
|
const migrationNames = input.getArgument('migration')
|
|
53
52
|
const migrations = []
|
|
54
53
|
for (const migrationName of migrationNames) {
|
|
55
|
-
const migration = await
|
|
54
|
+
const migration = await migrationsResolver.findSchemaMigrationByVersion(migrationName)
|
|
56
55
|
if (!migration) {
|
|
57
56
|
throw `Migration ${migrationName} not found`
|
|
58
57
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { Command, CommandConfiguration, Input } from '@contember/cli-common'
|
|
2
|
-
import { MigrationsContainerFactory } from '../../MigrationsContainer'
|
|
1
|
+
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
|
+
import { MigrationsContainerFactory } from '../../utils/migrations/MigrationsContainer'
|
|
3
3
|
import { interactiveResolveInstanceEnvironmentFromInput } from '../../utils/instance'
|
|
4
4
|
import { interactiveResolveApiToken } from '../../utils/tenant'
|
|
5
5
|
import { SystemClient } from '../../utils/system'
|
|
6
|
-
import { createMigrationStatusTable,
|
|
6
|
+
import { createMigrationStatusTable, MigrationState, sortMigrations } from '../../utils/migrations/migrations'
|
|
7
7
|
import chalk from 'chalk'
|
|
8
|
-
import {
|
|
8
|
+
import { resolveMigrationStatus } from '../../utils/migrations/MigrationExecuteHelper'
|
|
9
9
|
|
|
10
10
|
type Args = {
|
|
11
11
|
project?: string
|
|
@@ -66,9 +66,7 @@ export class MigrationStatusCommand extends Command<Args, Options> {
|
|
|
66
66
|
const remoteProject = input.getOption('remote-project') || project.name
|
|
67
67
|
const client = SystemClient.create(instance.baseUrl, remoteProject, apiToken)
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
let localMigrations = await container.migrationsResolver.getMigrations()
|
|
71
|
-
let status = getMigrationsStatus(executedMigrations, localMigrations)
|
|
69
|
+
let status = await resolveMigrationStatus(client, container.migrationsResolver)
|
|
72
70
|
const restoreMissing = input.getOption('restore-missing')
|
|
73
71
|
if (restoreMissing) {
|
|
74
72
|
const missing = status.errorMigrations.filter(it => it.state === MigrationState.EXECUTED_MISSING)
|
|
@@ -87,8 +85,7 @@ export class MigrationStatusCommand extends Command<Args, Options> {
|
|
|
87
85
|
fullMigration.name,
|
|
88
86
|
)
|
|
89
87
|
}
|
|
90
|
-
|
|
91
|
-
status = getMigrationsStatus(executedMigrations, localMigrations)
|
|
88
|
+
status = await resolveMigrationStatus(client, container.migrationsResolver)
|
|
92
89
|
}
|
|
93
90
|
|
|
94
91
|
const onlyErrors = input.getOption('only-errors')
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command, CommandConfiguration, Input, validateProjectName, Workspace } from '@contember/cli-common'
|
|
2
|
-
import { MigrationsContainerFactory } from '../../MigrationsContainer'
|
|
2
|
+
import { MigrationsContainerFactory } from '../../utils/migrations/MigrationsContainer'
|
|
3
3
|
import { validateSchemaAndPrintErrors } from '../../utils/schema'
|
|
4
4
|
import { emptySchema } from '@contember/schema-utils'
|
|
5
5
|
import { validateMigrations } from '../migrations/MigrationValidationHelper'
|
|
@@ -39,7 +39,7 @@ export class ProjectValidateCommand extends Command<Args, Options> {
|
|
|
39
39
|
console.group(`Project ${project.name}:`)
|
|
40
40
|
const migrationsDir = await project.migrationsDir
|
|
41
41
|
const container = new MigrationsContainerFactory(migrationsDir).create()
|
|
42
|
-
const migrations = await container.migrationsResolver.
|
|
42
|
+
const migrations = await container.migrationsResolver.getSchemaMigrations()
|
|
43
43
|
let projectValid = await validateMigrations(
|
|
44
44
|
emptySchema,
|
|
45
45
|
migrations,
|
package/src/run.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
DeployCommand,
|
|
6
6
|
InviteCommand,
|
|
7
7
|
MigrationAmendCommand,
|
|
8
|
+
MigrationBlankCommand,
|
|
8
9
|
MigrationDescribeCommand,
|
|
9
10
|
MigrationDiffCommand,
|
|
10
11
|
MigrationExecuteCommand,
|
|
@@ -35,6 +36,7 @@ import { checkVersions } from './utils/checkVersions';
|
|
|
35
36
|
['data:transfer']: () => new TransferCommand(workspace),
|
|
36
37
|
['migrations:diff']: () => new MigrationDiffCommand(workspace),
|
|
37
38
|
['migrations:amend']: () => new MigrationAmendCommand(workspace),
|
|
39
|
+
['migrations:blank']: () => new MigrationBlankCommand(workspace),
|
|
38
40
|
['migrations:describe']: () => new MigrationDescribeCommand(workspace),
|
|
39
41
|
['migrations:execute']: () => new MigrationExecuteCommand(workspace),
|
|
40
42
|
['migrations:rebase']: () => new MigrationRebaseCommand(workspace),
|
package/src/tsconfig.json
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
{ "path": "../../config-loader/src" },
|
|
13
13
|
{ "path": "../../database/src" },
|
|
14
14
|
{ "path": "../../dic/src" },
|
|
15
|
+
{ "path": "../../engine-common/src" },
|
|
15
16
|
{ "path": "../../engine-content-api/src" },
|
|
16
17
|
{ "path": "../../schema/src" },
|
|
17
18
|
{ "path": "../../schema-migrations/src" },
|
package/src/utils/admin.ts
CHANGED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild'
|
|
2
|
+
|
|
3
|
+
export const buildJs = async (path: string): Promise<string> => {
|
|
4
|
+
const response = await esbuild.build({
|
|
5
|
+
entryPoints: [path],
|
|
6
|
+
platform: 'node',
|
|
7
|
+
bundle: true,
|
|
8
|
+
treeShaking: true,
|
|
9
|
+
write: false,
|
|
10
|
+
loader: { '.sql': 'text' },
|
|
11
|
+
external: ['@contember/admin', 'react', 'react/jsx-runtime'],
|
|
12
|
+
})
|
|
13
|
+
return response.outputFiles[0].text
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { MigrationFileLoader } from './MigrationFileLoader'
|
|
2
|
+
import { buildJs } from '../esbuild'
|
|
3
|
+
import { MigrationParser } from './MigrationParser'
|
|
4
|
+
import { ContentMigration, MigrationContent, MigrationFile } from './MigrationFile'
|
|
5
|
+
|
|
6
|
+
export class JsLoader implements MigrationFileLoader {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly migrationParser: MigrationParser,
|
|
9
|
+
) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
public async load(file: MigrationFile): Promise<MigrationContent> {
|
|
14
|
+
const code = await buildJs(file.path)
|
|
15
|
+
const fn = new Function(`var module = {}; ((module) => { ${code} })(module); return module`)
|
|
16
|
+
const exports = fn().exports
|
|
17
|
+
if (!('default' in exports) && !('query' in exports) && !('queries' in exports)) {
|
|
18
|
+
throw `export "default" or "query" is required in ${file.path}`
|
|
19
|
+
}
|
|
20
|
+
if (typeof exports.default !== 'function') {
|
|
21
|
+
return this.migrationParser.parse(file, exports.default || exports)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
type: 'factory',
|
|
26
|
+
factory: async (): Promise<ContentMigration> => {
|
|
27
|
+
const result = await exports.default()
|
|
28
|
+
const migration = this.migrationParser.parse(file, result ?? { queries: [] })
|
|
29
|
+
if (migration.type !== 'content') {
|
|
30
|
+
throw new Error()
|
|
31
|
+
}
|
|
32
|
+
return migration
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { MigrationFileLoader } from './MigrationFileLoader'
|
|
2
|
+
import fs from 'node:fs/promises'
|
|
3
|
+
import { MigrationParser } from './MigrationParser'
|
|
4
|
+
import { MigrationFile } from './MigrationFile'
|
|
5
|
+
|
|
6
|
+
export class JsonLoader implements MigrationFileLoader {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly migrationParser: MigrationParser,
|
|
9
|
+
) {
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
public async load(file: MigrationFile) {
|
|
13
|
+
return this.migrationParser.parse(
|
|
14
|
+
file,
|
|
15
|
+
JSON.parse(await fs.readFile(file.path, { encoding: 'utf8' })),
|
|
16
|
+
)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { MigrationsContainerFactory } from '
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
SchemaMigrator,
|
|
9
|
-
SchemaVersionBuilder,
|
|
10
|
-
} from '@contember/schema-migrations'
|
|
11
|
-
import { Workspace, Project } from '@contember/cli-common'
|
|
1
|
+
import { Input, Project, Workspace } from '@contember/cli-common'
|
|
2
|
+
import { MigrationsContainerFactory } from './MigrationsContainer'
|
|
3
|
+
import { MigrationDescriber, SchemaMigrator } from '@contember/schema-migrations'
|
|
4
|
+
import { MigrationCreator } from './MigrationCreator'
|
|
5
|
+
import { MigrationsResolver } from './MigrationsResolver'
|
|
6
|
+
import { SchemaVersionBuilder } from './SchemaVersionBuilder'
|
|
7
|
+
import { MigrationFilesManager } from './MigrationFilesManager'
|
|
12
8
|
|
|
13
9
|
type Args = {
|
|
14
10
|
project?: string
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { MigrationFilesManager } from './MigrationFilesManager'
|
|
2
|
+
import { Schema } from '@contember/schema'
|
|
3
|
+
import { Migration, SchemaDiffer, VERSION_LATEST } from '@contember/schema-migrations'
|
|
4
|
+
import { MigrationVersionHelper } from '@contember/engine-common'
|
|
5
|
+
|
|
6
|
+
export class MigrationCreator {
|
|
7
|
+
constructor(
|
|
8
|
+
private readonly migrationFilesManager: MigrationFilesManager,
|
|
9
|
+
private readonly schemaDiffer: SchemaDiffer,
|
|
10
|
+
private readonly emptyTemplates: Record<string, string> = {},
|
|
11
|
+
) {}
|
|
12
|
+
|
|
13
|
+
async createEmptyMigrationFile(migrationName: string, format: string): Promise<string> {
|
|
14
|
+
if (!this.emptyTemplates.hasOwnProperty(format)) {
|
|
15
|
+
throw new Error(`Unknown format ${format}`)
|
|
16
|
+
}
|
|
17
|
+
await this.migrationFilesManager.createDirIfNotExist()
|
|
18
|
+
const [, fullName] = MigrationVersionHelper.createVersion(migrationName)
|
|
19
|
+
const filename = await this.migrationFilesManager.createFile(this.emptyTemplates[format], fullName, format)
|
|
20
|
+
|
|
21
|
+
return filename
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async prepareMigration(
|
|
25
|
+
initialSchema: Schema,
|
|
26
|
+
newSchema: Schema,
|
|
27
|
+
migrationName: string,
|
|
28
|
+
{ skipInitialSchemaValidation = false }: { skipInitialSchemaValidation?: boolean } = {},
|
|
29
|
+
): Promise<{ migration: Migration; initialSchema: Schema } | null> {
|
|
30
|
+
await this.migrationFilesManager.createDirIfNotExist()
|
|
31
|
+
|
|
32
|
+
const modifications = this.schemaDiffer.diffSchemas(initialSchema, newSchema, { skipInitialSchemaValidation })
|
|
33
|
+
if (modifications.length === 0) {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const migration = this.createMigration(modifications, migrationName)
|
|
38
|
+
|
|
39
|
+
return { initialSchema, migration }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async saveMigration(migration: Migration): Promise<string> {
|
|
43
|
+
const jsonDiff = MigrationCreator.createContent(migration)
|
|
44
|
+
const filename = await this.migrationFilesManager.createFile(jsonDiff, migration.name)
|
|
45
|
+
return filename
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async removeMigration(name: string): Promise<void> {
|
|
49
|
+
await this.migrationFilesManager.removeFile(name)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
private createMigration(modifications: Migration.Modification[], name: string): Migration {
|
|
53
|
+
const [version, fullName] = MigrationVersionHelper.createVersion(name)
|
|
54
|
+
return { formatVersion: VERSION_LATEST, modifications, version, name: fullName }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
public static createContent({ modifications, formatVersion }: Migration): string {
|
|
58
|
+
return JSON.stringify({ formatVersion, modifications }, undefined, '\t') + '\n'
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { CommandConfiguration } from '@contember/cli-common'
|
|
2
|
-
import { SystemClient } from '
|
|
3
|
-
import { getMigrationsStatus, MigrationToExecuteOkStatus, printMigrationDescription } from '
|
|
4
|
-
import { MigrationDescriber
|
|
2
|
+
import { SystemClient } from '../system'
|
|
3
|
+
import { getMigrationsStatus, MigrationToExecuteOkStatus, printMigrationDescription } from './migrations'
|
|
4
|
+
import { MigrationDescriber } from '@contember/schema-migrations'
|
|
5
5
|
import prompts from 'prompts'
|
|
6
|
+
import { MigrationsResolver } from './MigrationsResolver'
|
|
7
|
+
import { SchemaVersionBuilder } from './SchemaVersionBuilder'
|
|
8
|
+
import { ContentMigrationFactoryArgs, isSchemaMigration, ResolvedMigrationContent } from './MigrationFile'
|
|
9
|
+
import { assertNever } from '../assertNever'
|
|
6
10
|
|
|
7
11
|
export type ExecuteMigrationOptions = {
|
|
8
12
|
instance?: string
|
|
@@ -31,7 +35,7 @@ export const resolveMigrationStatus = async (
|
|
|
31
35
|
force: boolean = false,
|
|
32
36
|
): Promise<ReturnType<typeof getMigrationsStatus>> => {
|
|
33
37
|
const executedMigrations = await client.listExecutedMigrations()
|
|
34
|
-
const localMigrations = await migrationsResolver.
|
|
38
|
+
const localMigrations = await migrationsResolver.getMigrationFiles()
|
|
35
39
|
return getMigrationsStatus(executedMigrations, localMigrations, force)
|
|
36
40
|
}
|
|
37
41
|
|
|
@@ -41,6 +45,7 @@ export const executeMigrations = async ({
|
|
|
41
45
|
requireConfirmation,
|
|
42
46
|
schemaVersionBuilder,
|
|
43
47
|
migrationDescriber,
|
|
48
|
+
contentMigrationFactoryArgs,
|
|
44
49
|
force,
|
|
45
50
|
}: {
|
|
46
51
|
client: SystemClient
|
|
@@ -48,6 +53,7 @@ export const executeMigrations = async ({
|
|
|
48
53
|
requireConfirmation: boolean
|
|
49
54
|
schemaVersionBuilder: SchemaVersionBuilder
|
|
50
55
|
migrationDescriber: MigrationDescriber
|
|
56
|
+
contentMigrationFactoryArgs: ContentMigrationFactoryArgs
|
|
51
57
|
force?: boolean
|
|
52
58
|
}): Promise<number> => {
|
|
53
59
|
if (migrations.length === 0) {
|
|
@@ -73,7 +79,12 @@ export const executeMigrations = async ({
|
|
|
73
79
|
if (action === 'describe') {
|
|
74
80
|
const schema = await schemaVersionBuilder.buildSchemaUntil(migrations[0].version)
|
|
75
81
|
for (const migration of migrations) {
|
|
76
|
-
|
|
82
|
+
const migrationContent = await migration.localMigration.getContent()
|
|
83
|
+
if (!isSchemaMigration(migrationContent)) {
|
|
84
|
+
continue
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
await printMigrationDescription(migrationDescriber, schema, migrationContent, {
|
|
77
88
|
noSql: true,
|
|
78
89
|
})
|
|
79
90
|
}
|
|
@@ -85,10 +96,58 @@ export const executeMigrations = async ({
|
|
|
85
96
|
} while (true)
|
|
86
97
|
}
|
|
87
98
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
99
|
+
let migrationsToRun: ResolvedMigrationContent[] = []
|
|
100
|
+
|
|
101
|
+
const executeMigrations = async () => {
|
|
102
|
+
if (migrationsToRun.length === 0) {
|
|
103
|
+
return
|
|
104
|
+
}
|
|
105
|
+
await client.migrate(
|
|
106
|
+
migrationsToRun.map(it => {
|
|
107
|
+
if (it.type === 'schema') {
|
|
108
|
+
return {
|
|
109
|
+
version: it.version,
|
|
110
|
+
name: it.name,
|
|
111
|
+
type: 'SCHEMA',
|
|
112
|
+
schemaMigration: {
|
|
113
|
+
formatVersion: it.formatVersion,
|
|
114
|
+
modifications: it.modifications,
|
|
115
|
+
skippedErrors: it.skippedErrors,
|
|
116
|
+
},
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (it.type === 'content') {
|
|
120
|
+
return {
|
|
121
|
+
version: it.version,
|
|
122
|
+
name: it.name,
|
|
123
|
+
type: 'CONTENT',
|
|
124
|
+
contentMigration: it.queries,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return assertNever(it)
|
|
128
|
+
}),
|
|
129
|
+
force,
|
|
130
|
+
)
|
|
131
|
+
migrationsToRun.forEach(it => {
|
|
132
|
+
console.log(it.name)
|
|
133
|
+
})
|
|
134
|
+
migrationsToRun = []
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
console.log('Executing...')
|
|
138
|
+
|
|
139
|
+
for (const migration of migrations) {
|
|
140
|
+
const migrationContent = await migration.localMigration.getContent()
|
|
141
|
+
if (migrationContent.type === 'factory') {
|
|
142
|
+
await executeMigrations()
|
|
143
|
+
const result = await migrationContent.factory(contentMigrationFactoryArgs)
|
|
144
|
+
migrationsToRun.push(result)
|
|
145
|
+
await executeMigrations()
|
|
146
|
+
} else {
|
|
147
|
+
migrationsToRun.push(migrationContent)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
await executeMigrations()
|
|
92
151
|
console.log('Migration executed')
|
|
93
152
|
return 0
|
|
94
153
|
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Migration } from '@contember/schema-migrations'
|
|
2
|
+
import { JSONObject } from '@contember/cli-common'
|
|
3
|
+
|
|
4
|
+
export interface MigrationFile {
|
|
5
|
+
path: string
|
|
6
|
+
version: string
|
|
7
|
+
name: string
|
|
8
|
+
|
|
9
|
+
getContent: () => Promise<MigrationContent>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const isSchemaMigration = (content: MigrationContent): content is SchemaMigration => typeof content !== 'function' && 'modifications' in content
|
|
13
|
+
|
|
14
|
+
export type ResolvedMigrationContent =
|
|
15
|
+
| SchemaMigration
|
|
16
|
+
| ContentMigration
|
|
17
|
+
|
|
18
|
+
export type MigrationContent =
|
|
19
|
+
| ResolvedMigrationContent
|
|
20
|
+
| ContentMigrationFactory
|
|
21
|
+
|
|
22
|
+
export type SchemaMigration =
|
|
23
|
+
& {
|
|
24
|
+
type: 'schema'
|
|
25
|
+
}
|
|
26
|
+
& Migration
|
|
27
|
+
|
|
28
|
+
export type ContentMigration = {
|
|
29
|
+
type: 'content'
|
|
30
|
+
version: string
|
|
31
|
+
name: string
|
|
32
|
+
queries: ContentMigrationQuery[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ContentMigrationFactoryArgs = {
|
|
36
|
+
apiBaseUrl: string
|
|
37
|
+
projectName: string
|
|
38
|
+
apiToken: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export type ContentMigrationFactory = {
|
|
42
|
+
type: 'factory'
|
|
43
|
+
factory: (args: ContentMigrationFactoryArgs) => Promise<ContentMigration>
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type ContentMigrationQuery = {
|
|
47
|
+
query: string
|
|
48
|
+
variables?: JSONObject
|
|
49
|
+
stage?: string
|
|
50
|
+
checkMutationResult?: boolean
|
|
51
|
+
}
|
|
52
|
+
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { MigrationVersionHelper } from '@contember/engine-common'
|
|
2
|
+
import * as fs from 'node:fs/promises'
|
|
3
|
+
import { MigrationFileLoader } from './MigrationFileLoader'
|
|
4
|
+
import { MigrationContent, MigrationFile } from './MigrationFile'
|
|
5
|
+
import { join } from 'node:path'
|
|
6
|
+
import { Dirent } from 'node:fs'
|
|
7
|
+
|
|
8
|
+
export class MigrationFilesManager {
|
|
9
|
+
private migrations: null | Promise<MigrationFile[]> = null
|
|
10
|
+
|
|
11
|
+
constructor(
|
|
12
|
+
public readonly directory: string,
|
|
13
|
+
private readonly loaders: Record<string, MigrationFileLoader>,
|
|
14
|
+
) {}
|
|
15
|
+
|
|
16
|
+
public async createFile(content: string, name: string, extension = 'json'): Promise<string> {
|
|
17
|
+
this.migrations = null
|
|
18
|
+
const path = this.formatPath(name, extension)
|
|
19
|
+
await fs.writeFile(path, content, { encoding: 'utf8' })
|
|
20
|
+
return await fs.realpath(path)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
public async removeFile(name: string) {
|
|
24
|
+
this.migrations = null
|
|
25
|
+
const path = this.formatPath(name)
|
|
26
|
+
await fs.unlink(path)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public async moveFile(oldName: string, newName: string) {
|
|
30
|
+
this.migrations = null
|
|
31
|
+
await fs.rename(this.formatPath(oldName), this.formatPath(newName))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public async createDirIfNotExist(): Promise<void> {
|
|
35
|
+
try {
|
|
36
|
+
await fs.mkdir(this.directory)
|
|
37
|
+
} catch (e) {
|
|
38
|
+
if (!(e instanceof Error) || !('code' in e) || (e as any).code !== 'EEXIST') {
|
|
39
|
+
throw e
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
public async readFiles(): Promise<MigrationFile[]> {
|
|
45
|
+
return this.migrations ??= this.forceReadFiles()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
public async forceReadFiles(): Promise<MigrationFile[]> {
|
|
49
|
+
const files = (await this.listFiles()).map(it => ({
|
|
50
|
+
filename: it,
|
|
51
|
+
version: MigrationVersionHelper.extractVersion(it),
|
|
52
|
+
}))
|
|
53
|
+
|
|
54
|
+
return files.map(({ version, filename }): MigrationFile => {
|
|
55
|
+
const ext = filename.substring(filename.lastIndexOf('.') + 1)
|
|
56
|
+
const path = join(this.directory, filename)
|
|
57
|
+
let content: Promise<MigrationContent> | null = null
|
|
58
|
+
const file: MigrationFile = {
|
|
59
|
+
path: path,
|
|
60
|
+
version: version,
|
|
61
|
+
name: MigrationVersionHelper.extractName(path.substring(path.lastIndexOf('/') + 1)),
|
|
62
|
+
getContent: () => {
|
|
63
|
+
content ??= this.loaders[ext].load(file)
|
|
64
|
+
return content
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
return file
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private async tryReadDir(): Promise<Dirent[]> {
|
|
72
|
+
try {
|
|
73
|
+
return await fs.readdir(this.directory, { withFileTypes: true })
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (e instanceof Error && 'code' in e && (e as any).code === 'ENOENT') {
|
|
76
|
+
return []
|
|
77
|
+
}
|
|
78
|
+
throw e
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async listFiles(): Promise<string[]> {
|
|
83
|
+
const files = await this.tryReadDir()
|
|
84
|
+
|
|
85
|
+
const extensions = Object.keys(this.loaders).join('|')
|
|
86
|
+
const regex = new RegExp('^\\d{4}-\\d{2}-\\d{2}-\\d{6}-[\\w-]+\\.(' + extensions + ')$')
|
|
87
|
+
const filteredFiles = await Promise.all(
|
|
88
|
+
files
|
|
89
|
+
.filter(it => it.isFile())
|
|
90
|
+
.map(it => it.name)
|
|
91
|
+
.filter(it => it.match(regex)),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
return filteredFiles.sort()
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private formatPath(version: string, extension = 'json'): string {
|
|
98
|
+
const filename = `${version}.${extension}`
|
|
99
|
+
const path = `${this.directory}/${filename}`
|
|
100
|
+
return path
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { JSONValue } from '@contember/cli-common'
|
|
2
|
+
import { Migration, VERSION_INITIAL } from '@contember/schema-migrations'
|
|
3
|
+
import { ContentMigrationQuery, MigrationFile, ResolvedMigrationContent } from './MigrationFile'
|
|
4
|
+
|
|
5
|
+
export class MigrationParser {
|
|
6
|
+
public parse(migrationFile: MigrationFile, content: JSONValue): ResolvedMigrationContent {
|
|
7
|
+
if (typeof content !== 'object' || content === null) {
|
|
8
|
+
throw new Error('Invalid migration')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
if ('modifications' in content) {
|
|
12
|
+
const validated = content as Migration // todo validate
|
|
13
|
+
|
|
14
|
+
return {
|
|
15
|
+
type: 'schema',
|
|
16
|
+
version: migrationFile.version,
|
|
17
|
+
name: migrationFile.name,
|
|
18
|
+
formatVersion: validated.formatVersion || VERSION_INITIAL,
|
|
19
|
+
modifications: validated.modifications,
|
|
20
|
+
...(validated.skippedErrors ? { skippedErrors: validated.skippedErrors } : {}),
|
|
21
|
+
}
|
|
22
|
+
} else if ('queries' in content || 'query' in content) {
|
|
23
|
+
return {
|
|
24
|
+
type: 'content',
|
|
25
|
+
version: migrationFile.version,
|
|
26
|
+
name: migrationFile.name,
|
|
27
|
+
queries: ('queries' in content ? content.queries : [content]) as ContentMigrationQuery[],
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error('Invalid migration content')
|
|
31
|
+
}
|
|
32
|
+
}
|