@scandipwa/magento-scripts 2.4.11 → 2.4.12-alpha.0
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/index.js +11 -0
- package/lib/commands/cleanup.js +5 -1
- package/lib/commands/cli.js +6 -2
- package/lib/commands/import-db.js +13 -0
- package/lib/commands/link.js +12 -6
- package/lib/commands/logs.js +7 -1
- package/lib/commands/start.js +7 -2
- package/lib/commands/status.js +4 -1
- package/lib/commands/stop.js +9 -5
- package/lib/config/check-configuration-file.js +7 -5
- package/lib/config/docker.js +2 -1
- package/lib/config/get-magento-version-config.js +2 -0
- package/lib/tasks/database/create-magento-user.js +26 -6
- package/lib/tasks/database/fix-db.js +6 -4
- package/lib/tasks/database/import-dump-to-database.js +55 -48
- package/lib/tasks/database/import-remote-db/ssh/index.js +22 -16
- package/lib/tasks/database/import-remote-db/ssh/regular-server.js +7 -5
- package/lib/tasks/execute.js +3 -6
- package/lib/tasks/import-dump.js +18 -16
- package/lib/tasks/magento/enable-magento-composer-plugins.js +7 -5
- package/lib/tasks/magento/setup-magento/index-products.js +17 -15
- package/lib/tasks/magento/setup-magento/install-magento.js +35 -31
- package/lib/tasks/requirements/composer-credentials.js +41 -24
- package/lib/tasks/requirements/docker/context.js +24 -22
- package/lib/tasks/requirements/docker/permissions.js +12 -10
- package/lib/tasks/requirements/docker/running-status.js +31 -21
- package/lib/tasks/start.js +1 -1
- package/lib/util/ensure-agents-md.js +79 -0
- package/lib/util/execute-in-container.js +6 -12
- package/package.json +2 -2
- package/typings/context.d.ts +11 -0
package/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const logger = require('@scandipwa/scandipwa-dev-utils/logger')
|
|
|
6
6
|
const semver = require('semver')
|
|
7
7
|
const isInstalledGlobally = require('is-installed-globally')
|
|
8
8
|
const isRunningRoot = require('./lib/util/is-running-root')
|
|
9
|
+
const ensureAgentsMd = require('./lib/util/ensure-agents-md')
|
|
9
10
|
|
|
10
11
|
if (isRunningRoot()) {
|
|
11
12
|
logger.error('Root privileges detected!')
|
|
@@ -27,6 +28,8 @@ If you are experiencing problems with ${logger.style.misc(
|
|
|
27
28
|
process.exit(1)
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
ensureAgentsMd()
|
|
32
|
+
|
|
30
33
|
const commands = [
|
|
31
34
|
require('./lib/commands/link'),
|
|
32
35
|
require('./lib/commands/logs'),
|
|
@@ -123,6 +126,14 @@ const newVersionIsAPatch = (latestVersion, currentVersion) => {
|
|
|
123
126
|
yargs.scriptName('magento-scripts')
|
|
124
127
|
yargs.version(false)
|
|
125
128
|
|
|
129
|
+
yargs.option('silent', {
|
|
130
|
+
alias: 'q',
|
|
131
|
+
describe:
|
|
132
|
+
'Suppress all task progress output (Listr silent renderer). Enabled automatically when stdout is not a TTY.',
|
|
133
|
+
type: 'boolean',
|
|
134
|
+
default: false
|
|
135
|
+
})
|
|
136
|
+
|
|
126
137
|
// Initialize program commands
|
|
127
138
|
commands.forEach((command) => command(yargs))
|
|
128
139
|
|
package/lib/commands/cleanup.js
CHANGED
|
@@ -18,12 +18,16 @@ module.exports = (yargs) => {
|
|
|
18
18
|
}),
|
|
19
19
|
async (args) => {
|
|
20
20
|
logger.warn('you should not use this command.')
|
|
21
|
+
const silent = /** @type {boolean} */ (args.silent)
|
|
21
22
|
const tasks = new Listr(cleanup(), {
|
|
22
23
|
concurrent: false,
|
|
23
24
|
exitOnError: true,
|
|
24
25
|
ctx: {
|
|
25
|
-
force: args.force
|
|
26
|
+
force: args.force,
|
|
27
|
+
silent
|
|
26
28
|
},
|
|
29
|
+
renderer:
|
|
30
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
27
31
|
rendererOptions: { collapse: false }
|
|
28
32
|
})
|
|
29
33
|
|
package/lib/commands/cli.js
CHANGED
|
@@ -20,7 +20,8 @@ module.exports = (yargs) => {
|
|
|
20
20
|
'Enter CLI (magento, php, composer).',
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
22
22
|
() => {},
|
|
23
|
-
async () => {
|
|
23
|
+
async (args) => {
|
|
24
|
+
const silent = /** @type {boolean} */ (args.silent)
|
|
24
25
|
const tasks = new Listr(
|
|
25
26
|
[
|
|
26
27
|
getMagentoVersionConfig(),
|
|
@@ -33,8 +34,11 @@ module.exports = (yargs) => {
|
|
|
33
34
|
concurrent: false,
|
|
34
35
|
exitOnError: true,
|
|
35
36
|
ctx: {
|
|
36
|
-
throwMagentoVersionMissing: true
|
|
37
|
+
throwMagentoVersionMissing: true,
|
|
38
|
+
silent
|
|
37
39
|
},
|
|
40
|
+
renderer:
|
|
41
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
38
42
|
rendererOptions: { collapse: false, clearOutput: true }
|
|
39
43
|
}
|
|
40
44
|
)
|
|
@@ -27,12 +27,25 @@ module.exports = (yargs) => {
|
|
|
27
27
|
type: 'boolean',
|
|
28
28
|
default: false
|
|
29
29
|
})
|
|
30
|
+
yargs.option('non-interactive', {
|
|
31
|
+
alias: 'y',
|
|
32
|
+
describe:
|
|
33
|
+
'Skip all interactive prompts using sensible defaults',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false
|
|
36
|
+
})
|
|
30
37
|
},
|
|
31
38
|
async (args) => {
|
|
32
39
|
const tasks = new Listr(importDump(), {
|
|
33
40
|
exitOnError: true,
|
|
34
41
|
ctx: args,
|
|
35
42
|
concurrent: false,
|
|
43
|
+
renderer:
|
|
44
|
+
/** @type {boolean} */ (args.silent) ||
|
|
45
|
+
/** @type {boolean} */ (args.nonInteractive) ||
|
|
46
|
+
!process.stdout.isTTY
|
|
47
|
+
? 'silent'
|
|
48
|
+
: 'default',
|
|
36
49
|
rendererOptions: {
|
|
37
50
|
showErrorMessage: false,
|
|
38
51
|
showTimer: true
|
package/lib/commands/link.js
CHANGED
|
@@ -17,12 +17,18 @@ module.exports = (yargs) => {
|
|
|
17
17
|
default: false
|
|
18
18
|
}),
|
|
19
19
|
async (args) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
const silent = /** @type {boolean} */ (args.silent)
|
|
21
|
+
const tasks = new Listr(
|
|
22
|
+
linkTask(/** @type {string} */ (args.themepath)),
|
|
23
|
+
{
|
|
24
|
+
concurrent: false,
|
|
25
|
+
exitOnError: true,
|
|
26
|
+
ctx: { throwMagentoVersionMissing: true, silent },
|
|
27
|
+
renderer:
|
|
28
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
29
|
+
rendererOptions: { collapse: false }
|
|
30
|
+
}
|
|
31
|
+
)
|
|
26
32
|
|
|
27
33
|
try {
|
|
28
34
|
await tasks.run()
|
package/lib/commands/logs.js
CHANGED
|
@@ -70,6 +70,7 @@ npm run logs re (will match redis)`)
|
|
|
70
70
|
})
|
|
71
71
|
},
|
|
72
72
|
async (argv) => {
|
|
73
|
+
const silent = /** @type {boolean} */ (argv.silent)
|
|
73
74
|
const tasks = new Listr(
|
|
74
75
|
[
|
|
75
76
|
checkRequirements(),
|
|
@@ -82,7 +83,12 @@ npm run logs re (will match redis)`)
|
|
|
82
83
|
{
|
|
83
84
|
concurrent: false,
|
|
84
85
|
exitOnError: true,
|
|
85
|
-
ctx: {
|
|
86
|
+
ctx: {
|
|
87
|
+
throwMagentoVersionMissing: true,
|
|
88
|
+
silent
|
|
89
|
+
},
|
|
90
|
+
renderer:
|
|
91
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
86
92
|
rendererOptions: { collapse: false, clearOutput: true }
|
|
87
93
|
}
|
|
88
94
|
)
|
package/lib/commands/start.js
CHANGED
|
@@ -64,9 +64,9 @@ module.exports = (yargs) => {
|
|
|
64
64
|
type: 'number',
|
|
65
65
|
nargs: 1
|
|
66
66
|
})
|
|
67
|
-
.option('
|
|
67
|
+
.option('open', {
|
|
68
68
|
alias: 'n',
|
|
69
|
-
describe: '
|
|
69
|
+
describe: 'Open browser after command finished',
|
|
70
70
|
type: 'boolean',
|
|
71
71
|
default: false
|
|
72
72
|
})
|
|
@@ -109,6 +109,11 @@ module.exports = (yargs) => {
|
|
|
109
109
|
exitOnError: true,
|
|
110
110
|
ctx: args,
|
|
111
111
|
concurrent: false,
|
|
112
|
+
renderer:
|
|
113
|
+
/** @type {boolean} */ (args.silent) ||
|
|
114
|
+
!process.stdout.isTTY
|
|
115
|
+
? 'silent'
|
|
116
|
+
: 'default',
|
|
112
117
|
rendererOptions: {
|
|
113
118
|
showErrorMessage: false,
|
|
114
119
|
showTimer: true
|
package/lib/commands/status.js
CHANGED
|
@@ -23,6 +23,7 @@ module.exports = (yargs) => {
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
24
24
|
() => {},
|
|
25
25
|
async (args) => {
|
|
26
|
+
const silent = /** @type {boolean} */ (args.silent)
|
|
26
27
|
const tasks = new Listr(
|
|
27
28
|
[
|
|
28
29
|
checkRequirements(),
|
|
@@ -58,7 +59,9 @@ module.exports = (yargs) => {
|
|
|
58
59
|
{
|
|
59
60
|
concurrent: false,
|
|
60
61
|
exitOnError: false,
|
|
61
|
-
ctx: { throwMagentoVersionMissing: true, ...args },
|
|
62
|
+
ctx: { throwMagentoVersionMissing: true, ...args, silent },
|
|
63
|
+
renderer:
|
|
64
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
62
65
|
rendererOptions: { collapse: false, clearOutput: false }
|
|
63
66
|
}
|
|
64
67
|
)
|
package/lib/commands/stop.js
CHANGED
|
@@ -10,16 +10,20 @@ module.exports = (yargs) => {
|
|
|
10
10
|
'Stop the application.',
|
|
11
11
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
12
12
|
() => {},
|
|
13
|
-
async () => {
|
|
13
|
+
async (args) => {
|
|
14
|
+
const silent = /** @type {boolean} */ (args.silent)
|
|
14
15
|
const tasks = new Listr(stop(), {
|
|
15
16
|
concurrent: false,
|
|
16
17
|
exitOnError: true,
|
|
17
|
-
rendererOptions: {
|
|
18
|
-
collapse: false
|
|
19
|
-
},
|
|
20
18
|
ctx: {
|
|
21
19
|
throwMagentoVersionMissing: true,
|
|
22
|
-
projectPath: process.cwd()
|
|
20
|
+
projectPath: process.cwd(),
|
|
21
|
+
silent
|
|
22
|
+
},
|
|
23
|
+
renderer:
|
|
24
|
+
silent || !process.stdout.isTTY ? 'silent' : 'default',
|
|
25
|
+
rendererOptions: {
|
|
26
|
+
collapse: false
|
|
23
27
|
}
|
|
24
28
|
})
|
|
25
29
|
|
|
@@ -49,13 +49,15 @@ const checkConfigurationFile = () => ({
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
if (!magentoConfiguration) {
|
|
52
|
-
const magentoEdition =
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
const magentoEdition = ctx.nonInteractive
|
|
53
|
+
? 'Community'
|
|
54
|
+
: await task.prompt({
|
|
55
|
+
type: 'Select',
|
|
56
|
+
message: `Please select Magento edition you want to install.
|
|
55
57
|
|
|
56
58
|
Note that Enterprise edition requires Magento Enterprise License keys.`,
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
choices: ['Community', 'Enterprise']
|
|
60
|
+
})
|
|
59
61
|
|
|
60
62
|
magentoConfiguration = deepmerge(defaultMagentoConfig, {
|
|
61
63
|
edition: magentoEdition.toLowerCase()
|
package/lib/config/docker.js
CHANGED
|
@@ -232,7 +232,8 @@ module.exports = async (ctx, overridenConfiguration, baseConfig) => {
|
|
|
232
232
|
name: `${prefix}_php_with_xdebug`,
|
|
233
233
|
connectCommand: ['/bin/sh'],
|
|
234
234
|
execCommandEnv: {
|
|
235
|
-
XDEBUG_TRIGGER: 'PHPSTORM'
|
|
235
|
+
XDEBUG_TRIGGER: 'PHPSTORM',
|
|
236
|
+
XDEBUG_SESSION: 'PHPSTORM'
|
|
236
237
|
},
|
|
237
238
|
dependsOn: ['mariadb', 'redis', 'elasticsearch'],
|
|
238
239
|
user:
|
|
@@ -48,6 +48,8 @@ const getMagentoVersion = () => ({
|
|
|
48
48
|
const { magentoVersion: defaultMagentoVersion } =
|
|
49
49
|
defaultConfiguration
|
|
50
50
|
magentoVersion = defaultMagentoVersion
|
|
51
|
+
} else if (ctx.nonInteractive) {
|
|
52
|
+
magentoVersion = defaultConfiguration.magento
|
|
51
53
|
} else {
|
|
52
54
|
let promptSkipper = false
|
|
53
55
|
const timer = async () => {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const mysql2 = require('mysql2/promise')
|
|
2
|
+
const sleep = require('../../util/sleep')
|
|
2
3
|
const defaultMagentoUser = require('./default-magento-user')
|
|
3
4
|
|
|
4
5
|
/**
|
|
@@ -8,12 +9,31 @@ const createMagentoUser = () => ({
|
|
|
8
9
|
title: 'Creating Magento user',
|
|
9
10
|
task: async (ctx, task) => {
|
|
10
11
|
const { mariadb } = ctx.config.docker.getContainers()
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
|
|
13
|
+
/** @type {import('mysql2/promise').Connection | undefined} */
|
|
14
|
+
let connection
|
|
15
|
+
const maxTries = 20
|
|
16
|
+
|
|
17
|
+
for (let tries = 1; tries <= maxTries; tries++) {
|
|
18
|
+
try {
|
|
19
|
+
connection = await mysql2.createConnection({
|
|
20
|
+
host: '127.0.0.1',
|
|
21
|
+
port: ctx.ports.mariadb,
|
|
22
|
+
user: 'root',
|
|
23
|
+
password: mariadb.env.MARIADB_ROOT_PASSWORD
|
|
24
|
+
})
|
|
25
|
+
break
|
|
26
|
+
} catch (e) {
|
|
27
|
+
if (tries === maxTries) {
|
|
28
|
+
throw e
|
|
29
|
+
}
|
|
30
|
+
await sleep(1000)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!connection) {
|
|
35
|
+
throw new Error('Failed to connect to MariaDB')
|
|
36
|
+
}
|
|
17
37
|
|
|
18
38
|
const result = await connection.query(
|
|
19
39
|
'select Host, User from mysql.user;'
|
|
@@ -36,11 +36,13 @@ const fixDB = () => ({
|
|
|
36
36
|
title: 'Deleting customers data',
|
|
37
37
|
skip: ({ withCustomersData }) => withCustomersData,
|
|
38
38
|
task: async (ctx, subTask) => {
|
|
39
|
-
const deleteCustomerData =
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
const deleteCustomerData = ctx.nonInteractive
|
|
40
|
+
? false
|
|
41
|
+
: await subTask.prompt({
|
|
42
|
+
type: 'Confirm',
|
|
43
|
+
message: `Do you want to delete customers data (orders, customers and admin users) from this dump?
|
|
42
44
|
This will reduce database size and remove possible interference for your setup.`
|
|
43
|
-
|
|
45
|
+
})
|
|
44
46
|
|
|
45
47
|
if (!deleteCustomerData) {
|
|
46
48
|
subTask.skip()
|
|
@@ -60,25 +60,27 @@ const runSetGlobalLogBinTrustFunctionCreatorsCommand = () => ({
|
|
|
60
60
|
const deleteDatabaseBeforeImportingDumpPrompt = () => ({
|
|
61
61
|
title: 'Deleting magento database before importing dump',
|
|
62
62
|
task: async (ctx, task) => {
|
|
63
|
-
const deleteDatabaseMagentoChoice =
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
const deleteDatabaseMagentoChoice = ctx.nonInteractive
|
|
64
|
+
? 'delete'
|
|
65
|
+
: await task.prompt({
|
|
66
|
+
type: 'Select',
|
|
67
|
+
message: `Before importing database dump, would you like to delete existing database?
|
|
66
68
|
|
|
67
69
|
It is possible that dump might interfere with existing data in database.
|
|
68
70
|
|
|
69
71
|
Note that you will lose your existing database!`,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
choices: [
|
|
73
|
+
{
|
|
74
|
+
name: 'delete',
|
|
75
|
+
message: 'YES I WANT TO DELETE magento DATABASE!'
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'skip',
|
|
79
|
+
message:
|
|
80
|
+
"NO I DON'T WANT TO DELETE magento DATABASE! (Skip this step)"
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
})
|
|
82
84
|
|
|
83
85
|
if (deleteDatabaseMagentoChoice === 'delete') {
|
|
84
86
|
await ctx.databaseConnection.query(
|
|
@@ -103,22 +105,24 @@ const executeImportDumpSQL = () => ({
|
|
|
103
105
|
const { mariadb } = docker.getContainers(ports)
|
|
104
106
|
const { binFileName } = overridenConfiguration.configuration.mariadb
|
|
105
107
|
|
|
106
|
-
const userCredentialsForMariaDBCLI =
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
108
|
+
const userCredentialsForMariaDBCLI = ctx.nonInteractive
|
|
109
|
+
? `--user=root --password=${mariadb.env.MARIADB_ROOT_PASSWORD}`
|
|
110
|
+
: await task.prompt({
|
|
111
|
+
type: 'Select',
|
|
112
|
+
message: `Which user do you want to use to import db in ${mariadb._} client?`,
|
|
113
|
+
choices: [
|
|
114
|
+
{
|
|
115
|
+
name: `--user=root --password=${mariadb.env.MARIADB_ROOT_PASSWORD}`,
|
|
116
|
+
message: `root (${logger.style.command(
|
|
117
|
+
'Probably safest option'
|
|
118
|
+
)})`
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
name: `--user=${defaultMagentoUser.user} --password=${defaultMagentoUser.password}`,
|
|
122
|
+
message: `${defaultMagentoUser.user}`
|
|
123
|
+
}
|
|
124
|
+
]
|
|
125
|
+
})
|
|
122
126
|
|
|
123
127
|
const importCommand = `docker exec ${mariadb.name} bash -c "${binFileName} ${userCredentialsForMariaDBCLI} magento < ./dump.sql"`
|
|
124
128
|
|
|
@@ -137,11 +141,13 @@ const executeImportDumpSQL = () => ({
|
|
|
137
141
|
})
|
|
138
142
|
} catch (e) {
|
|
139
143
|
if (e.message.includes("Unknown collation: 'utf8mb4_0900_ai_ci'")) {
|
|
140
|
-
const confirmFixingCollation =
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
144
|
+
const confirmFixingCollation = ctx.nonInteractive
|
|
145
|
+
? 'yes'
|
|
146
|
+
: await task.prompt({
|
|
147
|
+
type: 'Select',
|
|
148
|
+
message: `We got the following error while trying to import ${logger.style.file(
|
|
149
|
+
'dump.sql'
|
|
150
|
+
)}!
|
|
145
151
|
|
|
146
152
|
${e.message}
|
|
147
153
|
|
|
@@ -150,18 +156,19 @@ ${logger.style.command(
|
|
|
150
156
|
"sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' dump.sql"
|
|
151
157
|
)}
|
|
152
158
|
`,
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
159
|
+
choices: [
|
|
160
|
+
{
|
|
161
|
+
name: 'yes',
|
|
162
|
+
message:
|
|
163
|
+
'Yes, run the following commands, I reaaaalllyy want dump to work! (this will not edit original dump.sql)'
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'no',
|
|
167
|
+
message:
|
|
168
|
+
'Okay, I got it. Will try to fix myself'
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
})
|
|
165
172
|
|
|
166
173
|
if (confirmFixingCollation === 'yes') {
|
|
167
174
|
task.output = 'Running fix command...'
|
|
@@ -19,11 +19,13 @@ const sshDb = () => ({
|
|
|
19
19
|
ctx.ssh = ssh
|
|
20
20
|
|
|
21
21
|
if (!password) {
|
|
22
|
-
const privateKey =
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
22
|
+
const privateKey = ctx.nonInteractive
|
|
23
|
+
? `${os.homedir()}/.ssh/id_rsa`
|
|
24
|
+
: await task.prompt({
|
|
25
|
+
type: 'Input',
|
|
26
|
+
message: `Please enter your private key location to connect to ${hostname}\n`,
|
|
27
|
+
initial: `${os.homedir()}/.ssh/id_rsa`
|
|
28
|
+
})
|
|
27
29
|
|
|
28
30
|
if (!(await pathExists(privateKey))) {
|
|
29
31
|
throw new KnownError(`Private key not found: ${privateKey}`)
|
|
@@ -31,11 +33,13 @@ const sshDb = () => ({
|
|
|
31
33
|
|
|
32
34
|
ctx.privateKey = privateKey
|
|
33
35
|
|
|
34
|
-
const passphrase =
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const passphrase = ctx.nonInteractive
|
|
37
|
+
? undefined
|
|
38
|
+
: await task.prompt({
|
|
39
|
+
type: 'Input',
|
|
40
|
+
message:
|
|
41
|
+
'Please enter your private key passphrase (if you have it)'
|
|
42
|
+
})
|
|
39
43
|
|
|
40
44
|
ctx.passphrase = passphrase || undefined
|
|
41
45
|
|
|
@@ -67,14 +71,16 @@ const sshDb = () => ({
|
|
|
67
71
|
const remoteFiles = remoteFilesOutput.split('\n')
|
|
68
72
|
|
|
69
73
|
if (dumpFileNames.every((dumpFile) => remoteFiles.includes(dumpFile))) {
|
|
70
|
-
ctx.makeRemoteDumps =
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
ctx.makeRemoteDumps = ctx.nonInteractive
|
|
75
|
+
? true
|
|
76
|
+
: await task.prompt({
|
|
77
|
+
type: 'Toggle',
|
|
78
|
+
enabled: 'Yes!',
|
|
79
|
+
disabled: 'No, just download and import them.',
|
|
80
|
+
message: `We found dump files on remote server.
|
|
75
81
|
Do you want to replace them with new dump files or use existing ones?
|
|
76
82
|
`
|
|
77
|
-
|
|
83
|
+
})
|
|
78
84
|
} else {
|
|
79
85
|
ctx.makeRemoteDumps = true
|
|
80
86
|
}
|
|
@@ -20,15 +20,17 @@ const regularSSHServer = () => ({
|
|
|
20
20
|
/**
|
|
21
21
|
* @type {string}
|
|
22
22
|
*/
|
|
23
|
-
const dumpCommand =
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
const dumpCommand = ctx.nonInteractive
|
|
24
|
+
? databaseDumpCommandWithOptions.join(' ')
|
|
25
|
+
: await task.prompt({
|
|
26
|
+
type: 'Input',
|
|
27
|
+
message: `Edit (if needed) command to connect to remote mysql server and create dump files.
|
|
26
28
|
Do not enter "--result-file" option, we need to control that part.
|
|
27
29
|
|
|
28
30
|
(documentation reference available here: https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html)
|
|
29
31
|
`,
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
initial: databaseDumpCommandWithOptions.join(' ')
|
|
33
|
+
})
|
|
32
34
|
|
|
33
35
|
if (dumpCommand.includes('--result-file')) {
|
|
34
36
|
throw new KnownError(
|
package/lib/tasks/execute.js
CHANGED
|
@@ -38,12 +38,9 @@ const executeTask = async (argv) => {
|
|
|
38
38
|
{
|
|
39
39
|
concurrent: false,
|
|
40
40
|
exitOnError: true,
|
|
41
|
-
ctx:
|
|
42
|
-
renderer:
|
|
43
|
-
? 'silent'
|
|
44
|
-
: process.stdout.isTTY
|
|
45
|
-
? 'default'
|
|
46
|
-
: 'silent',
|
|
41
|
+
ctx: { throwMagentoVersionMissing: true, nonInteractive },
|
|
42
|
+
renderer:
|
|
43
|
+
nonInteractive || !process.stdout.isTTY ? 'silent' : 'default',
|
|
47
44
|
rendererOptions: { collapse: false, clearOutput: true }
|
|
48
45
|
}
|
|
49
46
|
)
|
package/lib/tasks/import-dump.js
CHANGED
|
@@ -51,22 +51,24 @@ const importDump = () => ({
|
|
|
51
51
|
},
|
|
52
52
|
task: async (subCtx, subTask) => {
|
|
53
53
|
const doYouWantToRunSetupOnEmptyDB =
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
54
|
+
subCtx.nonInteractive
|
|
55
|
+
? 'skip'
|
|
56
|
+
: await subTask.prompt({
|
|
57
|
+
type: 'Select',
|
|
58
|
+
message: `We detected that Magento is not installed in database. Do you want to install Magento in database BEFORE importing database dump?`,
|
|
59
|
+
choices: [
|
|
60
|
+
{
|
|
61
|
+
name: 'try-install',
|
|
62
|
+
message:
|
|
63
|
+
'Try installing Magento before importing database'
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'skip',
|
|
67
|
+
message:
|
|
68
|
+
'Skip installing Magento and import database dump right away!'
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
})
|
|
70
72
|
|
|
71
73
|
if (doYouWantToRunSetupOnEmptyDB === 'skip') {
|
|
72
74
|
subTask.skip()
|
|
@@ -180,16 +180,18 @@ const enableMagentoComposerPlugins = () => ({
|
|
|
180
180
|
})
|
|
181
181
|
}
|
|
182
182
|
|
|
183
|
-
const answerForEnablingPlugins =
|
|
184
|
-
|
|
185
|
-
|
|
183
|
+
const answerForEnablingPlugins = ctx.nonInteractive
|
|
184
|
+
? 'all-individual'
|
|
185
|
+
: await task.prompt({
|
|
186
|
+
type: 'Select',
|
|
187
|
+
message: `Composer 2.2 requires manually allowing composer-plugins to run.
|
|
186
188
|
Magento requires the following plugins to correctly operate:
|
|
187
189
|
|
|
188
190
|
${missingPluginsFromAllowPlugins.map((p) => logger.style.code(p)).join('\n')}
|
|
189
191
|
|
|
190
192
|
Do you want to enable them all or disable some of them?`,
|
|
191
|
-
|
|
192
|
-
|
|
193
|
+
choices: pluginOptions
|
|
194
|
+
})
|
|
193
195
|
|
|
194
196
|
switch (answerForEnablingPlugins.toLowerCase()) {
|
|
195
197
|
case 'all': {
|
|
@@ -22,21 +22,23 @@ const indexProducts = () => ({
|
|
|
22
22
|
({ status }) => status !== 'valid'
|
|
23
23
|
)
|
|
24
24
|
|
|
25
|
-
const doYouWantToSkipIndexingPart =
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
const doYouWantToSkipIndexingPart = ctx.nonInteractive
|
|
26
|
+
? 'index'
|
|
27
|
+
: await task.prompt({
|
|
28
|
+
type: 'Select',
|
|
29
|
+
message: `Do you want to index the products? (There are ${invalidIndexers.length} invalid indexers, total indexers: ${data[0].length})\n`,
|
|
30
|
+
choices: [
|
|
31
|
+
{
|
|
32
|
+
name: 'index',
|
|
33
|
+
message: 'Yes, index them please'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'skip',
|
|
37
|
+
message:
|
|
38
|
+
'Skip, do not index them. I will do it later myself'
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
})
|
|
40
42
|
|
|
41
43
|
if (doYouWantToSkipIndexingPart === 'skip') {
|
|
42
44
|
task.skip()
|
|
@@ -51,26 +51,28 @@ const installMagento = ({ isDbEmpty = false } = {}) => ({
|
|
|
51
51
|
response && response.length > 0 && response[0]
|
|
52
52
|
|
|
53
53
|
if (usersWithUsernameAdmin && usersWithUsernameAdmin.length > 0) {
|
|
54
|
-
const confirmDeleteAdminUsers =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
54
|
+
const confirmDeleteAdminUsers = ctx.nonInteractive
|
|
55
|
+
? 'delete-all'
|
|
56
|
+
: await task.prompt({
|
|
57
|
+
type: 'Select',
|
|
58
|
+
message: `In order to install Magento in database you will need to delete admin user with username ${logger.style.command(
|
|
59
|
+
'admin'
|
|
60
|
+
)}`,
|
|
61
|
+
choices: [
|
|
62
|
+
{
|
|
63
|
+
name: 'delete-all',
|
|
64
|
+
message: `Delete all admin users (${logger.style.code(
|
|
65
|
+
'Recommended'
|
|
66
|
+
)})`
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'delete-only-admin',
|
|
70
|
+
message: `Delete only admin user with ${logger.style.command(
|
|
71
|
+
'admin'
|
|
72
|
+
)} username`
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
})
|
|
74
76
|
|
|
75
77
|
await databaseConnection.query('SET FOREIGN_KEY_CHECKS = 0;')
|
|
76
78
|
|
|
@@ -183,7 +185,7 @@ const installMagento = ({ isDbEmpty = false } = {}) => ({
|
|
|
183
185
|
--cache-backend='redis' \
|
|
184
186
|
--cache-backend-redis-server='${hostMachine}' \
|
|
185
187
|
--cache-backend-redis-port='${ports.redis}' \
|
|
186
|
-
--cache-backend-redis-db='0'
|
|
188
|
+
--cache-backend-redis-db='0' \
|
|
187
189
|
--db-host='${hostMachine}:${ports.mariadb}' \
|
|
188
190
|
--db-name='${defaultMagentoDatabase}' \
|
|
189
191
|
--db-user='${defaultMagentoUser.user}' \
|
|
@@ -219,21 +221,23 @@ const installMagento = ({ isDbEmpty = false } = {}) => ({
|
|
|
219
221
|
)
|
|
220
222
|
)
|
|
221
223
|
) {
|
|
222
|
-
const confirmToWipeEnvPhp =
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
const confirmToWipeEnvPhp = ctx.nonInteractive
|
|
225
|
+
? true
|
|
226
|
+
: await task.prompt({
|
|
227
|
+
type: 'Confirm',
|
|
228
|
+
message: `We detected that your encryption key in ${logger.style.file(
|
|
229
|
+
'app/etc/env.php'
|
|
230
|
+
)} file is not accepted by Magento installer.
|
|
227
231
|
To fix this issue we will need to ${logger.style.misc(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
+
'DELETE'
|
|
233
|
+
)} ${logger.style.file(
|
|
234
|
+
'app/etc/env.php'
|
|
235
|
+
)} file. It will be recreated but existing encryption key but if you any custom configuration in it will be lost.
|
|
232
236
|
|
|
233
237
|
Without this you will not be able to install Magento at this moment.
|
|
234
238
|
|
|
235
239
|
Do you want to continue?`
|
|
236
|
-
|
|
240
|
+
})
|
|
237
241
|
|
|
238
242
|
if (confirmToWipeEnvPhp) {
|
|
239
243
|
try {
|
|
@@ -209,23 +209,25 @@ const checkComposerCredentials = () => ({
|
|
|
209
209
|
|
|
210
210
|
if (composerAuthInRcFile) {
|
|
211
211
|
doConfigure = false
|
|
212
|
-
const loadCredentialsFrom =
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
212
|
+
const loadCredentialsFrom = ctx.nonInteractive
|
|
213
|
+
? true
|
|
214
|
+
: await task.prompt({
|
|
215
|
+
type: 'Confirm',
|
|
216
|
+
message: `We detected that you have ${logger.style.misc(
|
|
217
|
+
'COMPOSER_AUTH'
|
|
218
|
+
)} environment variable set in ${logger.style.file(
|
|
219
|
+
shellConfigFilePath
|
|
220
|
+
)} file,
|
|
219
221
|
but we do not see this variable inside ${logger.style.code(
|
|
220
|
-
|
|
221
|
-
|
|
222
|
+
'magento-scripts'
|
|
223
|
+
)} process.
|
|
222
224
|
|
|
223
225
|
${logger.style.misc(
|
|
224
226
|
"! Don't forget to reload your shell after process is finished !"
|
|
225
227
|
)}
|
|
226
228
|
|
|
227
229
|
Would you like to load them now?`
|
|
228
|
-
|
|
230
|
+
})
|
|
229
231
|
|
|
230
232
|
if (loadCredentialsFrom) {
|
|
231
233
|
const credentialsLine = lines.find((line) =>
|
|
@@ -249,6 +251,19 @@ Would you like to load them now?`
|
|
|
249
251
|
}
|
|
250
252
|
|
|
251
253
|
if (doConfigure) {
|
|
254
|
+
if (ctx.nonInteractive) {
|
|
255
|
+
throw new KnownError(
|
|
256
|
+
`Composer credentials are required but were not found in ${logger.style.misc(
|
|
257
|
+
'$COMPOSER_AUTH'
|
|
258
|
+
)} or ${logger.style.file('./auth.json')}.
|
|
259
|
+
|
|
260
|
+
Provide them non-interactively by setting the ${logger.style.misc(
|
|
261
|
+
'$COMPOSER_AUTH'
|
|
262
|
+
)} environment variable or by creating an ${logger.style.file(
|
|
263
|
+
'./auth.json'
|
|
264
|
+
)} file, then run the command again.`
|
|
265
|
+
)
|
|
266
|
+
}
|
|
252
267
|
return task.newListr(configureComposerCredentials())
|
|
253
268
|
}
|
|
254
269
|
}
|
|
@@ -306,20 +321,22 @@ Do you want to remove them now? File will be overwritten.`
|
|
|
306
321
|
: null
|
|
307
322
|
|
|
308
323
|
if (message) {
|
|
309
|
-
const response =
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
324
|
+
const response = ctx.nonInteractive
|
|
325
|
+
? 'skip'
|
|
326
|
+
: await task.prompt({
|
|
327
|
+
message,
|
|
328
|
+
type: 'Select',
|
|
329
|
+
choices: [
|
|
330
|
+
{
|
|
331
|
+
name: 'overwrite',
|
|
332
|
+
message: 'Yes, please!'
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: 'skip',
|
|
336
|
+
message: "No, I know what I'm doing"
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
})
|
|
323
340
|
|
|
324
341
|
if (response === 'overwrite') {
|
|
325
342
|
if (repoMagentoCredentials.username) {
|
|
@@ -48,29 +48,31 @@ const checkDockerDesktopContext = () => ({
|
|
|
48
48
|
return
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
const confirmContextChange =
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
51
|
+
const confirmContextChange = ctx.nonInteractive
|
|
52
|
+
? 'no'
|
|
53
|
+
: await task.prompt({
|
|
54
|
+
type: 'Select',
|
|
55
|
+
message: `Do you want to change current Docker Desktop context (${logger.style.code(
|
|
56
|
+
currentlyUsedContext.Name
|
|
57
|
+
)}) to ${logger.style.code('default')}?`,
|
|
58
|
+
choices: [
|
|
59
|
+
{
|
|
60
|
+
name: 'yes',
|
|
61
|
+
message: 'Yes'
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'no',
|
|
65
|
+
message:
|
|
66
|
+
"No, I don't know what this means, but you can ask again on next start."
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: 'skip',
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
message:
|
|
72
|
+
"I do know what this means and I DON'T want to change context for Docker. Also, save this answer to never ask again."
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
})
|
|
74
76
|
|
|
75
77
|
if (confirmContextChange === 'skip') {
|
|
76
78
|
cmaGlobalConfig.set(
|
|
@@ -21,22 +21,24 @@ const checkDockerSocketPermissions = () => ({
|
|
|
21
21
|
} catch (e) {
|
|
22
22
|
// check for permission
|
|
23
23
|
if (Math.abs(e.errno) === Math.abs(os.constants.errno.EACCES)) {
|
|
24
|
-
const confirmPrompt =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
const confirmPrompt = ctx.nonInteractive
|
|
25
|
+
? false
|
|
26
|
+
: await task.prompt({
|
|
27
|
+
type: 'Confirm',
|
|
28
|
+
message: `We detected that your Docker socket, located in ${logger.style.file(
|
|
29
|
+
dockerSocketPath
|
|
30
|
+
)}, have permissions set, that prevents user (${logger.style.misc(
|
|
31
|
+
os.userInfo().username
|
|
32
|
+
)}) from accessing it.
|
|
31
33
|
|
|
32
34
|
We can fix it by running the following command: ${logger.style.command(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
fixCommand
|
|
36
|
+
)}
|
|
35
37
|
|
|
36
38
|
Would you like to fix this permission issue?
|
|
37
39
|
|
|
38
40
|
Otherwise installation will likely fail.`
|
|
39
|
-
|
|
41
|
+
})
|
|
40
42
|
|
|
41
43
|
if (confirmPrompt) {
|
|
42
44
|
return task.newListr(executeSudoCommand(fixCommand))
|
|
@@ -61,11 +61,13 @@ const checkDockerStatusMacOS = () => ({
|
|
|
61
61
|
result.includes('Is the docker daemon running?') ||
|
|
62
62
|
result.includes('docker: command not found')
|
|
63
63
|
) {
|
|
64
|
-
const dockerOpenAppConfirmation =
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
const dockerOpenAppConfirmation = ctx.nonInteractive
|
|
65
|
+
? false
|
|
66
|
+
: await task.prompt({
|
|
67
|
+
type: 'Confirm',
|
|
68
|
+
message:
|
|
69
|
+
'Looks like Docker is not running, would you like us to open a Docker for Mac application and wait for it to start up?'
|
|
70
|
+
})
|
|
69
71
|
|
|
70
72
|
if (
|
|
71
73
|
dockerOpenAppConfirmation &&
|
|
@@ -136,12 +138,14 @@ const checkDockerStatusLinux = () => ({
|
|
|
136
138
|
|
|
137
139
|
if (engine.exists) {
|
|
138
140
|
if (!engine.isEnabled && !engine.isRunning) {
|
|
139
|
-
const dockerStartConfirmation =
|
|
140
|
-
|
|
141
|
-
|
|
141
|
+
const dockerStartConfirmation = ctx.nonInteractive
|
|
142
|
+
? false
|
|
143
|
+
: await task.prompt({
|
|
144
|
+
type: 'Confirm',
|
|
145
|
+
message: `Looks like Docker Engine is not enabled and not running, would you like to enable and run it?
|
|
142
146
|
|
|
143
147
|
This action requires root privileges.`
|
|
144
|
-
|
|
148
|
+
})
|
|
145
149
|
|
|
146
150
|
if (dockerStartConfirmation) {
|
|
147
151
|
await engine.service.enableAndStart()
|
|
@@ -150,12 +154,14 @@ const checkDockerStatusLinux = () => ({
|
|
|
150
154
|
}
|
|
151
155
|
task.skip('User skipped running Docker')
|
|
152
156
|
} else if (!engine.isRunning) {
|
|
153
|
-
const dockerStartConfirmation =
|
|
154
|
-
|
|
155
|
-
|
|
157
|
+
const dockerStartConfirmation = ctx.nonInteractive
|
|
158
|
+
? false
|
|
159
|
+
: await task.prompt({
|
|
160
|
+
type: 'Confirm',
|
|
161
|
+
message: `Looks like Docker Engine is not running, would you like to run it?
|
|
156
162
|
|
|
157
163
|
This action requires root privileges.`
|
|
158
|
-
|
|
164
|
+
})
|
|
159
165
|
|
|
160
166
|
if (dockerStartConfirmation) {
|
|
161
167
|
await engine.service.start()
|
|
@@ -166,12 +172,14 @@ const checkDockerStatusLinux = () => ({
|
|
|
166
172
|
}
|
|
167
173
|
} else if (desktop.exists) {
|
|
168
174
|
if (!desktop.isEnabled && !desktop.isRunning) {
|
|
169
|
-
const dockerStartConfirmation =
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
const dockerStartConfirmation = ctx.nonInteractive
|
|
176
|
+
? false
|
|
177
|
+
: await task.prompt({
|
|
178
|
+
type: 'Confirm',
|
|
179
|
+
message: `Looks like Docker Desktop is not enabled and not running, would you like to enable and run it?
|
|
172
180
|
|
|
173
181
|
This action requires root privileges.`
|
|
174
|
-
|
|
182
|
+
})
|
|
175
183
|
|
|
176
184
|
if (dockerStartConfirmation) {
|
|
177
185
|
await desktop.service.enableAndStart()
|
|
@@ -180,12 +188,14 @@ const checkDockerStatusLinux = () => ({
|
|
|
180
188
|
}
|
|
181
189
|
task.skip('User skipped running Docker')
|
|
182
190
|
} else if (!desktop.isRunning) {
|
|
183
|
-
const dockerStartConfirmation =
|
|
184
|
-
|
|
185
|
-
|
|
191
|
+
const dockerStartConfirmation = ctx.nonInteractive
|
|
192
|
+
? false
|
|
193
|
+
: await task.prompt({
|
|
194
|
+
type: 'Confirm',
|
|
195
|
+
message: `Looks like Docker Desktop is not running, would you like to run it?
|
|
186
196
|
|
|
187
197
|
This action requires root privileges.`
|
|
188
|
-
|
|
198
|
+
})
|
|
189
199
|
|
|
190
200
|
if (dockerStartConfirmation) {
|
|
191
201
|
await desktop.service.start()
|
package/lib/tasks/start.js
CHANGED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
|
|
4
|
+
const AGENTS_MD_CONTENT = `# CMA (magento-scripts) — AI/CI Reference
|
|
5
|
+
|
|
6
|
+
> Auto-generated by magento-scripts. Do not delete.
|
|
7
|
+
|
|
8
|
+
## Critical
|
|
9
|
+
|
|
10
|
+
- **Cannot run as root** — exits immediately with code 1, no override.
|
|
11
|
+
- **Non-TTY safe** — silent renderer activates automatically in CI/pipes; no \`-q\` needed.
|
|
12
|
+
- **Do NOT use \`--\` with exec** — it gets passed as the command and fails. Correct: \`magento-scripts exec php bin/magento cache:flush\`
|
|
13
|
+
- **\`cli\` is TTY-only** — use \`exec php bin/magento <cmd>\` in automation instead.
|
|
14
|
+
- **\`import-db\` is self-contained** — it stops running containers, assigns ports, starts services, waits for MariaDB, and imports. Do NOT run \`start\` before or between \`import-db\` attempts — that creates port conflicts. Just run \`import-db\` directly.
|
|
15
|
+
- **Do NOT run \`start\` then \`import-db\`** — each command manages its own container lifecycle. Running both creates split-brain port assignments. Use one or the other.
|
|
16
|
+
- **Long-running commands** — \`start\` and \`import-db\` can take 10+ minutes (container setup, large dumps). Set timeouts to at least 600000ms (10 min) or run without a timeout.
|
|
17
|
+
- **Shell escaping** — Avoid \`!\` in SQL strings passed via \`exec\` (e.g., \`!=\`), as the shell may interpret it. Use SQL alternatives like \`<>\` instead of \`!=\`.
|
|
18
|
+
|
|
19
|
+
## Commands
|
|
20
|
+
|
|
21
|
+
| Command | What it does | Key flags |
|
|
22
|
+
|---------|-------------|-----------|
|
|
23
|
+
| \`start\` | Start Docker environment | \`--no-open\`, \`--skip-setup\`, \`--port\` |
|
|
24
|
+
| \`stop\` | Stop all containers | — |
|
|
25
|
+
| \`status\` | Show container/DB status | — |
|
|
26
|
+
| \`exec <container> [cmd...]\` | Run command in container | use \`--\` before flags |
|
|
27
|
+
| \`import-db [file]\` | Import SQL dump into MariaDB | \`-y\` (non-interactive), \`--remote-db=ssh://user@host\` |
|
|
28
|
+
| \`logs <container>\` | Stream container logs | \`--tail N\`, \`--follow\` |
|
|
29
|
+
| \`cleanup\` | Remove cached/generated files | \`--force\` |
|
|
30
|
+
| \`cli\` | Interactive shell (TTY only) | — |
|
|
31
|
+
| \`link <path>\` | Link ScandiPWA theme | — |
|
|
32
|
+
|
|
33
|
+
## Containers
|
|
34
|
+
|
|
35
|
+
\`php\`, \`phpWithXdebug\`, \`nginx\`, \`sslTerminator\`, \`redis\`, \`mariadb\`, \`elasticsearch\`, \`maildev\`, \`varnish\` (if enabled)
|
|
36
|
+
|
|
37
|
+
## Examples
|
|
38
|
+
|
|
39
|
+
\`\`\`bash
|
|
40
|
+
magento-scripts start --no-open --skip-setup
|
|
41
|
+
magento-scripts import-db dump.sql -y
|
|
42
|
+
magento-scripts import-db -y --remote-db=ssh://user@host
|
|
43
|
+
magento-scripts exec php bin/magento cache:flush
|
|
44
|
+
magento-scripts exec php bin/magento indexer:reindex
|
|
45
|
+
|
|
46
|
+
# Query MariaDB (use mariadb binary, not mysql)
|
|
47
|
+
magento-scripts exec mariadb mariadb -u magento -pmagento magento -e "SELECT COUNT(*) FROM store"
|
|
48
|
+
magento-scripts logs magento --tail 100
|
|
49
|
+
magento-scripts stop
|
|
50
|
+
\`\`\`
|
|
51
|
+
`
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Write AGENTS.md and CLAUDE.md to the current working directory if they do not already exist.
|
|
55
|
+
* This gives AI agents and CI pipelines a command reference for the project.
|
|
56
|
+
*/
|
|
57
|
+
function ensureAgentsMd() {
|
|
58
|
+
const cwd = process.cwd()
|
|
59
|
+
|
|
60
|
+
const agentsDest = path.join(cwd, 'AGENTS.md')
|
|
61
|
+
if (!fs.existsSync(agentsDest)) {
|
|
62
|
+
try {
|
|
63
|
+
fs.writeFileSync(agentsDest, AGENTS_MD_CONTENT, 'utf8')
|
|
64
|
+
} catch (e) {
|
|
65
|
+
// Non-fatal — silently skip if the directory is not writable
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const claudeDest = path.join(cwd, 'CLAUDE.md')
|
|
70
|
+
if (!fs.existsSync(claudeDest)) {
|
|
71
|
+
try {
|
|
72
|
+
fs.writeFileSync(claudeDest, '@AGENTS.md\n', 'utf8')
|
|
73
|
+
} catch (e) {
|
|
74
|
+
// Non-fatal
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = ensureAgentsMd
|
|
@@ -30,18 +30,16 @@ const joinCommandArgs = (...args) =>
|
|
|
30
30
|
* @param {{ containerName: string, commands: string[], user?: string, env?: Record<string, string> }} param0
|
|
31
31
|
*/
|
|
32
32
|
const executeInContainer = ({ containerName, commands, user, env }) => {
|
|
33
|
-
if (!process.stdin.isTTY) {
|
|
34
|
-
process.stderr.write('This app works only in TTY mode')
|
|
35
|
-
process.exit(1)
|
|
36
|
-
}
|
|
37
33
|
const [commandBin, ...commandsArgs] = commands
|
|
38
34
|
|
|
35
|
+
const isTTY = process.stdin.isTTY
|
|
36
|
+
|
|
39
37
|
const execArgs = execCommand({
|
|
40
38
|
container: containerName,
|
|
41
39
|
command: commandBin,
|
|
42
40
|
user,
|
|
43
|
-
tty:
|
|
44
|
-
interactive:
|
|
41
|
+
tty: isTTY,
|
|
42
|
+
interactive: isTTY,
|
|
45
43
|
env: env || {}
|
|
46
44
|
})
|
|
47
45
|
const [command, ...args] = execArgs
|
|
@@ -91,18 +89,14 @@ const executeInContainerNonInteractive = async ({
|
|
|
91
89
|
* @param {string[]} commands
|
|
92
90
|
*/
|
|
93
91
|
const runInContainer = async (options, commands) => {
|
|
94
|
-
|
|
95
|
-
process.stderr.write('This app works only in TTY mode')
|
|
96
|
-
process.exit(1)
|
|
97
|
-
}
|
|
98
|
-
|
|
92
|
+
const isTTY = process.stdin.isTTY
|
|
99
93
|
const [commandBin, ...commandsArgs] = commands
|
|
100
94
|
|
|
101
95
|
const runResult = await run(
|
|
102
96
|
{
|
|
103
97
|
...options,
|
|
104
98
|
command: joinCommandArgs(commandBin, ...commandsArgs),
|
|
105
|
-
tty:
|
|
99
|
+
tty: isTTY,
|
|
106
100
|
detach: false,
|
|
107
101
|
rm: true
|
|
108
102
|
},
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "Scripts and configuration used by CMA.",
|
|
4
4
|
"homepage": "https://docs.create-magento-app.com/",
|
|
5
5
|
"repository": "github:scandipwa/create-magento-app",
|
|
6
|
-
"version": "2.4.
|
|
6
|
+
"version": "2.4.12-alpha.0",
|
|
7
7
|
"main": "./index.js",
|
|
8
8
|
"types": "./typings/index.d.ts",
|
|
9
9
|
"license": "OSL-3.0",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"@types/node": "^20.14.11",
|
|
60
60
|
"@types/yargs": "^17.0.32"
|
|
61
61
|
},
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "0fe2409c4e619ff985cf90a7f3d6cc4d45ca2552"
|
|
63
63
|
}
|
package/typings/context.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { PHPStormConfig } from './phpstorm'
|
|
|
8
8
|
|
|
9
9
|
export interface ListrContext {
|
|
10
10
|
noOpen?: boolean
|
|
11
|
+
open?: boolean
|
|
11
12
|
skipSetup?: boolean
|
|
12
13
|
resetGlobalConfig?: boolean
|
|
13
14
|
withCustomersData?: boolean
|
|
@@ -159,4 +160,14 @@ export interface ListrContext {
|
|
|
159
160
|
dockerClientData?: DockerVersionResult['Client']
|
|
160
161
|
dockerVersion?: DockerVersionResult['Server']['Version']
|
|
161
162
|
dockerMemoryLimit: number
|
|
163
|
+
silent?: boolean
|
|
164
|
+
nonInteractive?: boolean
|
|
165
|
+
deleteDb?: 'always' | 'never' | 'ask'
|
|
166
|
+
dbUser?: 'root' | 'magento'
|
|
167
|
+
fixCollation?: 'auto' | 'never' | 'ask'
|
|
168
|
+
installMagentoEmptyDb?: 'yes' | 'no' | 'ask'
|
|
169
|
+
privateKey?: string
|
|
170
|
+
passphrase?: string
|
|
171
|
+
makeRemoteDumps?: boolean
|
|
172
|
+
remoteDumpCommand?: string
|
|
162
173
|
}
|