@contember/cli 1.2.0-alpha.8 → 1.2.0-beta.1
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.map +1 -1
- package/dist/src/commands/deploy/DeployCommand.js +3 -10
- package/dist/src/commands/deploy/DeployCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationAmendCommand.js +1 -1
- package/dist/src/commands/migrations/MigrationAmendCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.d.ts +1 -0
- package/dist/src/commands/migrations/MigrationDiffCommand.d.ts.map +1 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.js +4 -1
- package/dist/src/commands/migrations/MigrationDiffCommand.js.map +1 -1
- package/dist/src/commands/migrations/MigrationValidationHelper.js +3 -3
- package/dist/src/commands/migrations/MigrationValidationHelper.js.map +1 -1
- package/dist/src/commands/project/ProjectGenerateDocumentation.js +3 -3
- package/dist/src/commands/project/ProjectGenerateDocumentation.js.map +1 -1
- package/dist/src/commands/transfer/ExportCommand.d.ts +17 -0
- package/dist/src/commands/transfer/ExportCommand.d.ts.map +1 -0
- package/dist/src/commands/transfer/ExportCommand.js +58 -0
- package/dist/src/commands/transfer/ExportCommand.js.map +1 -0
- package/dist/src/commands/transfer/ImportCommand.d.ts +16 -0
- package/dist/src/commands/transfer/ImportCommand.d.ts.map +1 -0
- package/dist/src/commands/transfer/ImportCommand.js +54 -0
- package/dist/src/commands/transfer/ImportCommand.js.map +1 -0
- package/dist/src/commands/transfer/TransferCommand.d.ts +17 -0
- package/dist/src/commands/transfer/TransferCommand.d.ts.map +1 -0
- package/dist/src/commands/transfer/TransferCommand.js +60 -0
- package/dist/src/commands/transfer/TransferCommand.js.map +1 -0
- package/dist/src/commands/transfer/index.d.ts +4 -0
- package/dist/src/commands/transfer/index.d.ts.map +1 -0
- package/dist/src/commands/transfer/index.js +20 -0
- package/dist/src/commands/transfer/index.js.map +1 -0
- package/dist/src/commands/transfer/utils.d.ts +26 -0
- package/dist/src/commands/transfer/utils.d.ts.map +1 -0
- package/dist/src/commands/transfer/utils.js +134 -0
- package/dist/src/commands/transfer/utils.js.map +1 -0
- package/dist/src/run.js +6 -0
- package/dist/src/run.js.map +1 -1
- package/dist/src/tsconfig.tsbuildinfo +1 -1
- package/dist/src/utils/admin.js +1 -1
- package/dist/src/utils/admin.js.map +1 -1
- package/dist/src/utils/commands.d.ts +2 -2
- package/dist/src/utils/commands.d.ts.map +1 -1
- package/dist/src/utils/commands.js +2 -2
- package/dist/src/utils/commands.js.map +1 -1
- package/dist/src/utils/dockerCompose.js +5 -5
- package/dist/src/utils/dockerCompose.js.map +1 -1
- package/dist/src/utils/dsn.d.ts +6 -0
- package/dist/src/utils/dsn.d.ts.map +1 -0
- package/dist/src/utils/dsn.js +17 -0
- package/dist/src/utils/dsn.js.map +1 -0
- package/dist/src/utils/npm.js +8 -8
- package/dist/src/utils/npm.js.map +1 -1
- package/dist/src/utils/project/loadSchema.js +2 -2
- package/dist/src/utils/project/loadSchema.js.map +1 -1
- package/dist/src/utils/project/projectDescribe.js +1 -1
- package/dist/src/utils/project/projectDescribe.js.map +1 -1
- package/dist/src/utils/project/resources/out.css +1 -8
- package/dist/src/utils/schema.d.ts +2 -2
- package/dist/src/utils/schema.d.ts.map +1 -1
- package/dist/src/utils/schema.js +3 -3
- package/dist/src/utils/schema.js.map +1 -1
- package/dist/src/utils/stdio.d.ts +2 -0
- package/dist/src/utils/stdio.d.ts.map +1 -0
- package/dist/src/utils/stdio.js +10 -0
- package/dist/src/utils/stdio.js.map +1 -0
- package/dist/src/utils/stream.d.ts +9 -0
- package/dist/src/utils/stream.d.ts.map +1 -0
- package/dist/src/utils/stream.js +41 -0
- package/dist/src/utils/stream.js.map +1 -0
- package/dist/src/utils/workspace.d.ts.map +1 -1
- package/dist/src/utils/workspace.js +1 -2
- package/dist/src/utils/workspace.js.map +1 -1
- package/dist/src/utils/yaml.d.ts +1 -1
- package/dist/src/utils/yaml.d.ts.map +1 -1
- package/dist/src/utils/yaml.js +3 -3
- package/dist/src/utils/yaml.js.map +1 -1
- package/package.json +11 -12
- package/src/commands/deploy/DeployCommand.ts +4 -10
- package/src/commands/migrations/MigrationAmendCommand.ts +1 -1
- package/src/commands/migrations/MigrationDiffCommand.ts +6 -1
- package/src/commands/migrations/MigrationValidationHelper.ts +3 -3
- package/src/commands/project/ProjectGenerateDocumentation.ts +2 -2
- package/src/commands/transfer/ExportCommand.ts +72 -0
- package/src/commands/transfer/ImportCommand.ts +61 -0
- package/src/commands/transfer/TransferCommand.ts +71 -0
- package/src/commands/transfer/index.ts +3 -0
- package/src/commands/transfer/utils.ts +135 -0
- package/src/run.ts +6 -0
- package/src/utils/admin.ts +1 -1
- package/src/utils/commands.ts +2 -2
- package/src/utils/dockerCompose.ts +2 -2
- package/src/utils/dsn.ts +13 -0
- package/src/utils/npm.ts +2 -2
- package/src/utils/project/loadSchema.ts +1 -1
- package/src/utils/project/projectDescribe.tsx +1 -1
- package/src/utils/schema.ts +4 -4
- package/src/utils/stdio.ts +6 -0
- package/src/utils/stream.ts +44 -0
- package/src/utils/workspace.ts +1 -2
- package/src/utils/yaml.ts +2 -3
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
|
+
import { createWriteStream } from 'node:fs'
|
|
3
|
+
import { dataExport, resolveProject } from './utils'
|
|
4
|
+
import { maskToken } from '../../utils/token'
|
|
5
|
+
import { pipeline } from 'node:stream/promises'
|
|
6
|
+
import { rewriteStdoutLine } from '../../utils/stdio'
|
|
7
|
+
|
|
8
|
+
type Args = {
|
|
9
|
+
source?: string
|
|
10
|
+
}
|
|
11
|
+
type Options = {
|
|
12
|
+
'include-system'?: boolean
|
|
13
|
+
'no-gzip'?: boolean
|
|
14
|
+
output?: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ExportCommand extends Command<Args, Options> {
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly workspace: Workspace,
|
|
20
|
+
) {
|
|
21
|
+
super()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected configure(configuration: CommandConfiguration<Args, Options>): void {
|
|
25
|
+
configuration.description('Export data from a project')
|
|
26
|
+
const from = configuration.argument('source')
|
|
27
|
+
if (this.workspace.isSingleProjectMode()) {
|
|
28
|
+
from.optional()
|
|
29
|
+
}
|
|
30
|
+
configuration.option('include-system').valueNone()
|
|
31
|
+
configuration.option('no-gzip').valueNone()
|
|
32
|
+
configuration.option('output').valueRequired()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected async execute(input: Input<Args, Options>): Promise<void | number> {
|
|
36
|
+
const from = input.getArgument('source')
|
|
37
|
+
const project = await resolveProject({ workspace: this.workspace, projectOrDsn: from })
|
|
38
|
+
|
|
39
|
+
console.log('')
|
|
40
|
+
console.log('Exporting data from a following project')
|
|
41
|
+
console.log('')
|
|
42
|
+
console.log(`Project name: ${project.project}`)
|
|
43
|
+
console.log(`API URL: ${project.baseUrl}`)
|
|
44
|
+
console.log(`Token: ${maskToken(project.token)}`)
|
|
45
|
+
|
|
46
|
+
const noGzip = input.getOption('no-gzip') === true
|
|
47
|
+
const includeSystem = input.getOption('include-system') === true
|
|
48
|
+
|
|
49
|
+
const response = await dataExport({ project, includeSystem, noGzip })
|
|
50
|
+
|
|
51
|
+
let transferred = 0
|
|
52
|
+
let start = Date.now()
|
|
53
|
+
let lastMbReported = 0
|
|
54
|
+
console.log('')
|
|
55
|
+
response.on('data', chunk => {
|
|
56
|
+
transferred += chunk.length
|
|
57
|
+
const currentMb = Math.floor(transferred / 1024 / 1024)
|
|
58
|
+
if (currentMb > lastMbReported) {
|
|
59
|
+
const durationS = Math.floor((Date.now() - start) / 1000)
|
|
60
|
+
rewriteStdoutLine(`transferred ${currentMb} MiB; ${durationS} seconds`)
|
|
61
|
+
lastMbReported = currentMb
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const fileName = input.getOption('output') ?? `${project.project}.jsonl${noGzip ? '' : '.gz'}`
|
|
66
|
+
const fileStream = createWriteStream(fileName)
|
|
67
|
+
|
|
68
|
+
await pipeline(response, fileStream)
|
|
69
|
+
|
|
70
|
+
console.log('\nExport done.')
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
|
+
import { createReadStream } from 'node:fs'
|
|
3
|
+
import { createGunzip } from 'node:zlib'
|
|
4
|
+
import { confirmImport, dataImport, resolveProject } from './utils'
|
|
5
|
+
import { maskToken } from '../../utils/token'
|
|
6
|
+
import { rewriteStdoutLine } from '../../utils/stdio'
|
|
7
|
+
import { readStream } from '../../utils/stream'
|
|
8
|
+
|
|
9
|
+
type Args = {
|
|
10
|
+
file: string
|
|
11
|
+
target?: string
|
|
12
|
+
}
|
|
13
|
+
type Options = {
|
|
14
|
+
'yes': boolean
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export class ImportCommand extends Command<Args, Options> {
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly workspace: Workspace,
|
|
20
|
+
) {
|
|
21
|
+
super()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected configure(configuration: CommandConfiguration<Args, Options>): void {
|
|
25
|
+
configuration.description('Import data into a project')
|
|
26
|
+
configuration.argument('file')
|
|
27
|
+
const from = configuration.argument('target')
|
|
28
|
+
if (this.workspace.isSingleProjectMode()) {
|
|
29
|
+
from.optional()
|
|
30
|
+
}
|
|
31
|
+
configuration.option('yes').valueNone()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected async execute(input: Input<Args, Options>): Promise<void | number> {
|
|
35
|
+
const from = input.getArgument('target')
|
|
36
|
+
const project = await resolveProject({ projectOrDsn: from, workspace: this.workspace })
|
|
37
|
+
console.log('')
|
|
38
|
+
console.log('Importing data into a following project')
|
|
39
|
+
console.log('')
|
|
40
|
+
console.log(`Project name: ${project.project}`)
|
|
41
|
+
console.log(`API URL: ${project.baseUrl}`)
|
|
42
|
+
console.log(`Token: ${maskToken(project.token)}`)
|
|
43
|
+
console.log('')
|
|
44
|
+
if (!await confirmImport(input)) {
|
|
45
|
+
return 1
|
|
46
|
+
}
|
|
47
|
+
const file = input.getArgument('file')
|
|
48
|
+
const baseInputStream = createReadStream(file)
|
|
49
|
+
const stream = (file.endsWith('.gz') ? baseInputStream.pipe(createGunzip()) : baseInputStream)
|
|
50
|
+
const response = await dataImport({ stream: stream, project, printProgress: rewriteStdoutLine })
|
|
51
|
+
console.log('')
|
|
52
|
+
const responseData = JSON.parse((await readStream(response)).toString())
|
|
53
|
+
if (responseData.ok) {
|
|
54
|
+
console.log('Import done.')
|
|
55
|
+
} else {
|
|
56
|
+
console.error('Import failed:')
|
|
57
|
+
console.log(responseData)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { Command, CommandConfiguration, Input, Workspace } from '@contember/cli-common'
|
|
2
|
+
import { confirmImport, dataExport, dataImport, resolveProject } from './utils'
|
|
3
|
+
import { maskToken } from '../../utils/token'
|
|
4
|
+
import { rewriteStdoutLine } from '../../utils/stdio'
|
|
5
|
+
import { readStream } from '../../utils/stream'
|
|
6
|
+
|
|
7
|
+
type Args = {
|
|
8
|
+
source: string
|
|
9
|
+
target: string
|
|
10
|
+
}
|
|
11
|
+
type Options = {
|
|
12
|
+
'include-system'?: boolean
|
|
13
|
+
yes: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class TransferCommand extends Command<Args, Options> {
|
|
17
|
+
constructor(
|
|
18
|
+
private readonly workspace: Workspace,
|
|
19
|
+
) {
|
|
20
|
+
super()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected configure(configuration: CommandConfiguration<Args, Options>): void {
|
|
24
|
+
configuration.description('Transfer data between projects')
|
|
25
|
+
configuration.argument('source')
|
|
26
|
+
const target = configuration.argument('target')
|
|
27
|
+
if (this.workspace.isSingleProjectMode()) {
|
|
28
|
+
target.optional()
|
|
29
|
+
}
|
|
30
|
+
configuration.option('include-system').valueNone()
|
|
31
|
+
configuration.option('yes').valueNone()
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
protected async execute(input: Input<Args, Options>): Promise<void | number> {
|
|
35
|
+
const source = input.getArgument('source')
|
|
36
|
+
const sourceProject = await resolveProject({ projectOrDsn: source, workspace: this.workspace })
|
|
37
|
+
|
|
38
|
+
const target = input.getArgument('target')
|
|
39
|
+
const targetProject = await resolveProject({ projectOrDsn: target, workspace: this.workspace })
|
|
40
|
+
|
|
41
|
+
console.log('')
|
|
42
|
+
console.log('Transferring data between projects:')
|
|
43
|
+
console.log('')
|
|
44
|
+
console.log(`Source project name: ${sourceProject.project}`)
|
|
45
|
+
console.log(`Source API URL: ${sourceProject.baseUrl}`)
|
|
46
|
+
console.log(`Source token: ${maskToken(sourceProject.token)}`)
|
|
47
|
+
console.log('')
|
|
48
|
+
console.log(`Target project name: ${targetProject.project}`)
|
|
49
|
+
console.log(`Target API URL: ${targetProject.baseUrl}`)
|
|
50
|
+
console.log(`Target token: ${maskToken(targetProject.token)}`)
|
|
51
|
+
console.log('')
|
|
52
|
+
if (!await confirmImport(input)) {
|
|
53
|
+
return 1
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const includeSystem = input.getOption('include-system') === true
|
|
57
|
+
const exportResponse = (await dataExport({ project: sourceProject, includeSystem, noGzip: true }))
|
|
58
|
+
const importResponse = await dataImport({
|
|
59
|
+
stream: exportResponse, project: targetProject, printProgress: rewriteStdoutLine,
|
|
60
|
+
})
|
|
61
|
+
console.log('')
|
|
62
|
+
|
|
63
|
+
const responseData = JSON.parse((await readStream(importResponse)).toString())
|
|
64
|
+
if (responseData.ok) {
|
|
65
|
+
console.log('Transfer done.')
|
|
66
|
+
} else {
|
|
67
|
+
console.error('Transfer failed:')
|
|
68
|
+
console.log(responseData)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Readable, Stream } from 'node:stream'
|
|
2
|
+
import { LineTransform, readStream } from '../../utils/stream'
|
|
3
|
+
import https from 'node:https'
|
|
4
|
+
import http from 'node:http'
|
|
5
|
+
import { Input, Workspace } from '@contember/cli-common'
|
|
6
|
+
import { parseDsn } from '../../utils/dsn'
|
|
7
|
+
import { interactiveResolveInstanceEnvironmentFromInput } from '../../utils/instance'
|
|
8
|
+
import { interactiveResolveApiToken } from '../../utils/tenant'
|
|
9
|
+
import prompts from 'prompts'
|
|
10
|
+
|
|
11
|
+
export type ProjectEndpoint = {
|
|
12
|
+
token: string
|
|
13
|
+
project: string
|
|
14
|
+
baseUrl: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const resolveProject = async ({ projectOrDsn, workspace }: { projectOrDsn?: string; workspace: Workspace }): Promise<ProjectEndpoint> => {
|
|
18
|
+
let endpoint: string | undefined = undefined
|
|
19
|
+
let token: string | undefined = undefined
|
|
20
|
+
let project: string | undefined
|
|
21
|
+
if (projectOrDsn?.includes('://')) {
|
|
22
|
+
({ endpoint, project, token } = parseDsn(projectOrDsn))
|
|
23
|
+
} else if (projectOrDsn !== undefined && projectOrDsn !== '.') {
|
|
24
|
+
project = projectOrDsn
|
|
25
|
+
}
|
|
26
|
+
const instance = await interactiveResolveInstanceEnvironmentFromInput(workspace, endpoint)
|
|
27
|
+
token ??= await interactiveResolveApiToken({ workspace, instance })
|
|
28
|
+
project ??= (await workspace.projects.getSingleProject()).name
|
|
29
|
+
return { project, token, baseUrl: instance.baseUrl }
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const dataImport = async ({ printProgress, stream, project: { project, token, baseUrl } }: { stream: Readable; project: ProjectEndpoint; printProgress: (message: string) => void }) => {
|
|
34
|
+
let table = ''
|
|
35
|
+
let rowCount = 0
|
|
36
|
+
let rowTotal = 0
|
|
37
|
+
let transferred = 0
|
|
38
|
+
let start = Date.now()
|
|
39
|
+
const inputStream = stream.pipe(new LineTransform(line => {
|
|
40
|
+
transferred += line.length + 1
|
|
41
|
+
if (line === '') {
|
|
42
|
+
return ''
|
|
43
|
+
}
|
|
44
|
+
const data = JSON.parse(line)
|
|
45
|
+
switch (data[0]) {
|
|
46
|
+
case 'importSystemSchemaBegin':
|
|
47
|
+
return JSON.stringify([data[0], { ...data[1], project }])
|
|
48
|
+
case 'importContentSchemaBegin':
|
|
49
|
+
return JSON.stringify([data[0], { ...data[1], project }])
|
|
50
|
+
case 'insertBegin':
|
|
51
|
+
table = data[1].table
|
|
52
|
+
rowCount = 0
|
|
53
|
+
break
|
|
54
|
+
case 'insertRow':
|
|
55
|
+
rowCount++
|
|
56
|
+
rowTotal++
|
|
57
|
+
break
|
|
58
|
+
case 'insertEnd':
|
|
59
|
+
break
|
|
60
|
+
}
|
|
61
|
+
if ((rowCount % 1000) === 0 || data[0] !== 'insertRow') {
|
|
62
|
+
const mbs = Math.floor(transferred / 1024 / 1024).toString()
|
|
63
|
+
const durationS = Math.floor((Date.now() - start) / 1000)
|
|
64
|
+
printProgress(`transferred ${mbs} MiB; inserted ${rowCount} ${table} rows, ${rowTotal} total; ${durationS} seconds`)
|
|
65
|
+
}
|
|
66
|
+
return line
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
return await executeRequest({
|
|
70
|
+
url: `${baseUrl}/import`,
|
|
71
|
+
token,
|
|
72
|
+
headers: {
|
|
73
|
+
'Content-type': 'application/x-ndjson',
|
|
74
|
+
},
|
|
75
|
+
body: inputStream,
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const dataExport = async ({ project: { project, token, baseUrl }, noGzip, includeSystem }: { project: ProjectEndpoint; includeSystem: boolean; noGzip: boolean }) => {
|
|
80
|
+
return await executeRequest({
|
|
81
|
+
url: `${baseUrl}/export`,
|
|
82
|
+
token,
|
|
83
|
+
headers: {
|
|
84
|
+
'Accept-Encoding': noGzip ? 'identity' : 'gzip',
|
|
85
|
+
'Content-type': 'application/json',
|
|
86
|
+
},
|
|
87
|
+
body: JSON.stringify({
|
|
88
|
+
projects: [{ slug: project, system: includeSystem }],
|
|
89
|
+
}),
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const executeRequest = async ({ url, token, headers, body }: { url: string; token: string; headers: Record<string, string >; body: any }) => {
|
|
94
|
+
return await new Promise<http.IncomingMessage>((resolve, reject) => {
|
|
95
|
+
const req = (url.startsWith('https://') ? https : http).request(url, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: {
|
|
98
|
+
Authorization: `Bearer ${token}`,
|
|
99
|
+
...headers,
|
|
100
|
+
},
|
|
101
|
+
}, async res => {
|
|
102
|
+
if (res.statusCode !== 200) {
|
|
103
|
+
const responseBody = (await readStream(res)).toString()
|
|
104
|
+
reject(new Error(`Invalid response: ${res.statusMessage}\n${responseBody}`))
|
|
105
|
+
} else {
|
|
106
|
+
resolve(res)
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
if (body instanceof Stream) {
|
|
111
|
+
body.pipe(req)
|
|
112
|
+
} else {
|
|
113
|
+
req.write(body)
|
|
114
|
+
req.end()
|
|
115
|
+
}
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export const confirmImport = async (input: Input<{}, { yes: boolean }>): Promise<boolean> => {
|
|
120
|
+
if (input.getOption('yes')) {
|
|
121
|
+
return true
|
|
122
|
+
}
|
|
123
|
+
if (!process.stdin.isTTY) {
|
|
124
|
+
throw 'TTY not available. Pass --yes option to confirm execution.'
|
|
125
|
+
}
|
|
126
|
+
console.log('This will completely wipe the target project.')
|
|
127
|
+
console.log('(to skip this dialog, you can pass --yes option)')
|
|
128
|
+
console.log('')
|
|
129
|
+
const { ok } = await prompts({
|
|
130
|
+
type: 'confirm',
|
|
131
|
+
name: 'ok',
|
|
132
|
+
message: `Do you want to continue?`,
|
|
133
|
+
})
|
|
134
|
+
return ok
|
|
135
|
+
}
|
package/src/run.ts
CHANGED
|
@@ -19,12 +19,18 @@ import {
|
|
|
19
19
|
VersionCommand,
|
|
20
20
|
WorkspaceUpdateApiCommand,
|
|
21
21
|
} from './commands'
|
|
22
|
+
import { ExportCommand } from './commands/transfer/ExportCommand'
|
|
23
|
+
import { ImportCommand } from './commands/transfer/ImportCommand'
|
|
24
|
+
import { TransferCommand } from './commands/transfer/TransferCommand';
|
|
22
25
|
|
|
23
26
|
(async () => {
|
|
24
27
|
const workspace = await Workspace.get(process.cwd())
|
|
25
28
|
const commands: CommandFactoryList = {
|
|
26
29
|
['deploy']: () => new DeployCommand(workspace),
|
|
27
30
|
['version']: () => new VersionCommand(),
|
|
31
|
+
['data:export']: () => new ExportCommand(workspace),
|
|
32
|
+
['data:import']: () => new ImportCommand(workspace),
|
|
33
|
+
['data:transfer']: () => new TransferCommand(workspace),
|
|
28
34
|
['migrations:diff']: () => new MigrationDiffCommand(workspace),
|
|
29
35
|
['migrations:amend']: () => new MigrationAmendCommand(workspace),
|
|
30
36
|
['migrations:describe']: () => new MigrationDescribeCommand(workspace),
|
package/src/utils/admin.ts
CHANGED
package/src/utils/commands.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
|
|
2
|
-
import { Readable, Writable } from 'stream'
|
|
1
|
+
import { ChildProcessWithoutNullStreams, spawn } from 'node:child_process'
|
|
2
|
+
import { Readable, Writable } from 'node:stream'
|
|
3
3
|
import chalk from 'chalk'
|
|
4
4
|
|
|
5
5
|
export type RunningCommand = { child: ChildProcessWithoutNullStreams; output: Promise<string> }
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { join } from 'path'
|
|
2
|
-
import { pathExists } from '
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { pathExists } from '@contember/cli-common'
|
|
3
3
|
import { JsonUpdateCallback, updateYaml } from './yaml'
|
|
4
4
|
|
|
5
5
|
const MAIN_CONFIGS = ['docker-compose.yaml', 'docker-compose.yml']
|
package/src/utils/dsn.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { URL } from 'node:url'
|
|
2
|
+
|
|
3
|
+
export const parseDsn = (dsn: string): {endpoint: string; project: string; token: string} => {
|
|
4
|
+
const uri = new URL(dsn)
|
|
5
|
+
if (uri.protocol !== 'contember:' && uri.protocol !== 'contember-unsecure:') {
|
|
6
|
+
throw 'Invalid DSN'
|
|
7
|
+
}
|
|
8
|
+
return {
|
|
9
|
+
project: uri.username,
|
|
10
|
+
token: uri.password,
|
|
11
|
+
endpoint: (uri.protocol === 'contember-unsecure:' ? 'http://' : 'https://') + uri.host,
|
|
12
|
+
}
|
|
13
|
+
}
|
package/src/utils/npm.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Project } from '@contember/cli-common'
|
|
2
2
|
import { Schema } from '@contember/schema'
|
|
3
3
|
import * as esbuild from 'esbuild'
|
|
4
|
-
import { join } from 'path'
|
|
4
|
+
import { join } from 'node:path'
|
|
5
5
|
import { NodeVM } from 'vm2'
|
|
6
6
|
import { schemaType } from '@contember/schema-utils'
|
|
7
7
|
|
|
@@ -2,7 +2,7 @@ import { Schema } from '@contember/schema'
|
|
|
2
2
|
import { h } from 'preact'
|
|
3
3
|
import render from 'preact-render-to-string'
|
|
4
4
|
import { ProjectInfo } from './components/ProjectInfo'
|
|
5
|
-
import { readFile } from 'fs/promises'
|
|
5
|
+
import { readFile } from 'node:fs/promises'
|
|
6
6
|
|
|
7
7
|
export const renderProjectInfoHtml = async (schema: Schema, projectName: string) => {
|
|
8
8
|
const html = render(<ProjectInfo schema={schema} projectName={projectName} />)
|
package/src/utils/schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SchemaValidator, ValidationError } from '@contember/schema-utils'
|
|
1
|
+
import { SchemaValidator, SchemaValidatorSkippedErrors, ValidationError } from '@contember/schema-utils'
|
|
2
2
|
import { Schema } from '@contember/schema'
|
|
3
3
|
|
|
4
4
|
export const printValidationErrors = (errors: ValidationError[], message?: string) => {
|
|
@@ -7,13 +7,13 @@ export const printValidationErrors = (errors: ValidationError[], message?: strin
|
|
|
7
7
|
}
|
|
8
8
|
console.group(message || 'Schema is invalid:')
|
|
9
9
|
for (const err of errors) {
|
|
10
|
-
console.log(err.path.join('.')
|
|
10
|
+
console.log(`${err.path.join('.')}: [${err.code}] ${err.message}`)
|
|
11
11
|
}
|
|
12
12
|
console.groupEnd()
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
export const validateSchemaAndPrintErrors = (schema: Schema, message?: string): boolean => {
|
|
16
|
-
const errors = SchemaValidator.validate(schema)
|
|
15
|
+
export const validateSchemaAndPrintErrors = (schema: Schema, message?: string, skippedErrors?: SchemaValidatorSkippedErrors[]): boolean => {
|
|
16
|
+
const errors = SchemaValidator.validate(schema, skippedErrors)
|
|
17
17
|
printValidationErrors(errors, message)
|
|
18
18
|
return errors.length === 0
|
|
19
19
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Stream, Transform } from 'node:stream'
|
|
2
|
+
import { Buffer } from 'node:buffer'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export class LineTransform extends Transform {
|
|
6
|
+
private chunks: Buffer[] = []
|
|
7
|
+
|
|
8
|
+
constructor(transform: (line: string) => string) {
|
|
9
|
+
super({
|
|
10
|
+
transform: (chunk, encoding, done) => {
|
|
11
|
+
while (true) {
|
|
12
|
+
const eolIndex = chunk.indexOf('\n')
|
|
13
|
+
|
|
14
|
+
if (eolIndex < 0) {
|
|
15
|
+
this.chunks.push(chunk)
|
|
16
|
+
break
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.chunks.push(chunk.slice(0, eolIndex))
|
|
20
|
+
chunk = chunk.slice(eolIndex + 1)
|
|
21
|
+
this.push(transform(Buffer.concat(this.chunks).toString('utf8')) + '\n', 'utf8')
|
|
22
|
+
this.chunks = []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return done()
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
flush: done => {
|
|
29
|
+
if (this.chunks.length) {
|
|
30
|
+
this.push(transform(Buffer.concat(this.chunks).toString('utf8')), 'utf8')
|
|
31
|
+
return done()
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
export const readStream = async (stream: Stream) => new Promise<Buffer>((resolve, reject) => {
|
|
40
|
+
const chunks: Buffer[] = []
|
|
41
|
+
stream.on('error', err => reject(err))
|
|
42
|
+
stream.on('data', chunk => chunks.push(chunk))
|
|
43
|
+
stream.on('end', () => resolve(Buffer.concat(chunks)))
|
|
44
|
+
})
|
package/src/utils/workspace.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { pathExists } from '
|
|
2
|
-
import { formatWorkspaceConfigPath, Workspace } from '@contember/cli-common'
|
|
1
|
+
import { formatWorkspaceConfigPath, pathExists, Workspace } from '@contember/cli-common'
|
|
3
2
|
import { updateYaml } from './yaml'
|
|
4
3
|
|
|
5
4
|
export const updateWorkspaceApiVersion = async (workspace: Workspace, newVersion: string): Promise<string | null> => {
|
package/src/utils/yaml.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { JSONObject, JSONPrimitive } from '@contember/cli-common
|
|
2
|
-
import { pathExists } from 'fs-extra'
|
|
1
|
+
import { JSONObject, JSONPrimitive, pathExists } from '@contember/cli-common'
|
|
3
2
|
import YAWN from 'yawn-yaml/cjs'
|
|
4
3
|
import { Merger } from '@contember/config-loader'
|
|
5
4
|
import jsyaml from 'js-yaml'
|
|
6
|
-
import { readFile, writeFile } from 'fs/promises'
|
|
5
|
+
import { readFile, writeFile } from 'node:fs/promises'
|
|
7
6
|
|
|
8
7
|
export type JsonUpdateCallback<Value = JSONObject> = (
|
|
9
8
|
data: Partial<Value>,
|