@scandipwa/magento-scripts 2.4.11 → 2.4.12

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 CHANGED
@@ -123,6 +123,14 @@ const newVersionIsAPatch = (latestVersion, currentVersion) => {
123
123
  yargs.scriptName('magento-scripts')
124
124
  yargs.version(false)
125
125
 
126
+ yargs.option('silent', {
127
+ alias: 'q',
128
+ describe:
129
+ 'Suppress all task progress output (Listr silent renderer). Enabled automatically when stdout is not a TTY.',
130
+ type: 'boolean',
131
+ default: false
132
+ })
133
+
126
134
  // Initialize program commands
127
135
  commands.forEach((command) => command(yargs))
128
136
 
@@ -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
 
@@ -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
@@ -17,12 +17,18 @@ module.exports = (yargs) => {
17
17
  default: false
18
18
  }),
19
19
  async (args) => {
20
- const tasks = new Listr(linkTask(args.themepath), {
21
- concurrent: false,
22
- exitOnError: true,
23
- ctx: { throwMagentoVersionMissing: true },
24
- rendererOptions: { collapse: false }
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()
@@ -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: { throwMagentoVersionMissing: true },
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
  )
@@ -64,9 +64,9 @@ module.exports = (yargs) => {
64
64
  type: 'number',
65
65
  nargs: 1
66
66
  })
67
- .option('no-open', {
67
+ .option('open', {
68
68
  alias: 'n',
69
- describe: 'Do not open browser after command finished',
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
@@ -3,7 +3,7 @@ const { Listr } = require('listr2')
3
3
  const getMagentoVersionConfig = require('../config/get-magento-version-config')
4
4
  const { getCachedPorts } = require('../config/get-port-config')
5
5
 
6
- const { prettyStatus } = require('../tasks/status')
6
+ const { prettyStatus, simpleStatus } = require('../tasks/status')
7
7
  const { checkRequirements } = require('../tasks/requirements')
8
8
  const { statusContainers } = require('../tasks/docker/containers')
9
9
  const getProjectConfiguration = require('../config/get-project-configuration')
@@ -20,9 +20,35 @@ module.exports = (yargs) => {
20
20
  yargs.command(
21
21
  'status',
22
22
  'Show application status',
23
- // eslint-disable-next-line @typescript-eslint/no-empty-function
24
- () => {},
23
+ (yargs) => {
24
+ yargs
25
+ .option('non-interactive', {
26
+ alias: 'n',
27
+ type: 'boolean',
28
+ default: false,
29
+ description:
30
+ 'Print a plain-text status summary (for AI terminals and scripts)'
31
+ })
32
+ .option('verbose', {
33
+ alias: 'v',
34
+ type: 'boolean',
35
+ default: false,
36
+ description:
37
+ 'Retrieve Docker image and volume sizes (slower, off by default)'
38
+ })
39
+ },
25
40
  async (args) => {
41
+ const silent = /** @type {boolean} */ (args.silent)
42
+ // A non-TTY stdout (pipe, CI, AI terminal) is inherently
43
+ // non-interactive; the -n flag forces it even inside a TTY.
44
+ const nonInteractive =
45
+ !!(args.nonInteractive || args.n) || !process.stdout.isTTY
46
+ // Enumerating per-image/-volume sizes shells out to
47
+ // `docker system df --verbose`, which is slow; skip it by default.
48
+ // The non-interactive report already prints the full status; the
49
+ // --verbose flag only adds the Docker image/volume sizes (and is
50
+ // what the pretty renderer needs to render them at all).
51
+ const verbose = !!(args.verbose || args.v)
26
52
  const tasks = new Listr(
27
53
  [
28
54
  checkRequirements(),
@@ -39,6 +65,9 @@ module.exports = (yargs) => {
39
65
  checkSearchEngineVersion(),
40
66
  {
41
67
  title: 'Retrieving Docker System data',
68
+ skip: () =>
69
+ !verbose &&
70
+ 'Docker image and volume sizes omitted (pass --verbose to include them)',
42
71
  task: async (ctx) => {
43
72
  ctx.systemDFData =
44
73
  await systemApi.df({
@@ -58,13 +87,25 @@ module.exports = (yargs) => {
58
87
  {
59
88
  concurrent: false,
60
89
  exitOnError: false,
61
- ctx: { throwMagentoVersionMissing: true, ...args },
90
+ ctx: {
91
+ throwMagentoVersionMissing: true,
92
+ ...args,
93
+ silent,
94
+ nonInteractive,
95
+ verbose
96
+ },
97
+ renderer: silent || nonInteractive ? 'silent' : 'default',
62
98
  rendererOptions: { collapse: false, clearOutput: false }
63
99
  }
64
100
  )
65
101
 
66
102
  try {
67
- await prettyStatus(await tasks.run())
103
+ const ctx = await tasks.run()
104
+ if (nonInteractive) {
105
+ simpleStatus(ctx)
106
+ } else {
107
+ await prettyStatus(ctx)
108
+ }
68
109
  } catch (e) {
69
110
  logger.error(e.message || e)
70
111
  process.exit(1)
@@ -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 = await task.prompt({
53
- type: 'Select',
54
- message: `Please select Magento edition you want to install.
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
- choices: ['Community', 'Enterprise']
58
- })
59
+ choices: ['Community', 'Enterprise']
60
+ })
59
61
 
60
62
  magentoConfiguration = deepmerge(defaultMagentoConfig, {
61
63
  edition: magentoEdition.toLowerCase()
@@ -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
- const connection = await mysql2.createConnection({
12
- host: '127.0.0.1',
13
- port: ctx.ports.mariadb,
14
- user: 'root',
15
- password: mariadb.env.MARIADB_ROOT_PASSWORD
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 = await subTask.prompt({
40
- type: 'Confirm',
41
- message: `Do you want to delete customers data (orders, customers and admin users) from this dump?
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 = await task.prompt({
64
- type: 'Select',
65
- message: `Before importing database dump, would you like to delete existing database?
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
- choices: [
71
- {
72
- name: 'delete',
73
- message: 'YES I WANT TO DELETE magento DATABASE!'
74
- },
75
- {
76
- name: 'skip',
77
- message:
78
- "NO I DON'T WANT TO DELETE magento DATABASE! (Skip this step)"
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 = await task.prompt({
107
- type: 'Select',
108
- message: `Which user do you want to use to import db in ${mariadb._} client?`,
109
- choices: [
110
- {
111
- name: `--user=root --password=${mariadb.env.MARIADB_ROOT_PASSWORD}`,
112
- message: `root (${logger.style.command(
113
- 'Probably safest option'
114
- )})`
115
- },
116
- {
117
- name: `--user=${defaultMagentoUser.user} --password=${defaultMagentoUser.password}`,
118
- message: `${defaultMagentoUser.user}`
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 = await task.prompt({
141
- type: 'Select',
142
- message: `We got the following error while trying to import ${logger.style.file(
143
- 'dump.sql'
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
- choices: [
154
- {
155
- name: 'yes',
156
- message:
157
- 'Yes, run the following commands, I reaaaalllyy want dump to work! (this will not edit original dump.sql)'
158
- },
159
- {
160
- name: 'no',
161
- message: 'Okay, I got it. Will try to fix myself'
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 = await task.prompt({
23
- type: 'Input',
24
- message: `Please enter your private key location to connect to ${hostname}\n`,
25
- initial: `${os.homedir()}/.ssh/id_rsa`
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 = await task.prompt({
35
- type: 'Input',
36
- message:
37
- 'Please enter your private key passphrase (if you have it)'
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 = await task.prompt({
71
- type: 'Toggle',
72
- enabled: 'Yes!',
73
- disabled: 'No, just download and import them.',
74
- message: `We found dump files on remote server.
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 = await task.prompt({
24
- type: 'Input',
25
- message: `Edit (if needed) command to connect to remote mysql server and create dump files.
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
- initial: databaseDumpCommandWithOptions.join(' ')
31
- })
32
+ initial: databaseDumpCommandWithOptions.join(' ')
33
+ })
32
34
 
33
35
  if (dumpCommand.includes('--result-file')) {
34
36
  throw new KnownError(
@@ -38,12 +38,9 @@ const executeTask = async (argv) => {
38
38
  {
39
39
  concurrent: false,
40
40
  exitOnError: true,
41
- ctx: /** @type {any} */ ({ throwMagentoVersionMissing: true }),
42
- renderer: nonInteractive
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
  )