@scandipwa/magento-scripts 1.16.0-alpha.4 → 2.0.0-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.
Files changed (190) hide show
  1. package/lib/commands/cli.js +2 -4
  2. package/lib/commands/execute.js +28 -3
  3. package/lib/commands/start.js +37 -15
  4. package/lib/commands/status.js +2 -0
  5. package/lib/config/docker.js +65 -42
  6. package/lib/config/get-project-configuration.js +3 -1
  7. package/lib/config/index.js +10 -7
  8. package/lib/config/magento/required-php-extensions/index.js +29 -0
  9. package/lib/config/magento/required-php-extensions/magento-2.3.js +19 -0
  10. package/lib/config/magento/required-php-extensions/magento-2.4.js +29 -0
  11. package/lib/config/php/base-repo.js +3 -0
  12. package/lib/config/php/extensions/apcu.js +12 -0
  13. package/lib/config/php/extensions/bcmath.js +7 -0
  14. package/lib/config/php/extensions/curl.js +11 -0
  15. package/lib/config/php/extensions/gd.js +14 -0
  16. package/lib/config/php/extensions/index.js +18 -2
  17. package/lib/config/php/extensions/intl.js +10 -0
  18. package/lib/config/php/extensions/opcache.js +10 -0
  19. package/lib/config/php/extensions/pdo_mysql.js +7 -0
  20. package/lib/config/php/extensions/soap.js +10 -0
  21. package/lib/config/php/extensions/sodium.js +10 -0
  22. package/lib/config/php/extensions/xdebug.js +8 -5
  23. package/lib/config/php/extensions/xsl.js +10 -0
  24. package/lib/config/php/extensions/zip.js +10 -0
  25. package/lib/config/php/{releases → versions}/index.js +0 -0
  26. package/lib/config/php/versions/php-7.2.js +23 -0
  27. package/lib/config/php/versions/php-7.3.js +23 -0
  28. package/lib/config/php/versions/php-7.4.js +23 -0
  29. package/lib/config/php/versions/php-8.1.js +23 -0
  30. package/lib/config/php-config.js +8 -12
  31. package/lib/config/port-config.js +1 -5
  32. package/lib/config/templates/magentorc.template +3 -3
  33. package/lib/config/templates/php-fpm.template.conf +3 -3
  34. package/lib/config/templates/php.template.ini +12 -6
  35. package/lib/config/templates/ssl-terminator.template.conf +1 -1
  36. package/lib/config/templates/vscode-launch.template.json +3 -1
  37. package/lib/config/versions/magento-2.3.0.js +8 -2
  38. package/lib/config/versions/magento-2.3.1.js +8 -2
  39. package/lib/config/versions/magento-2.3.2-p1.js +44 -0
  40. package/lib/config/versions/magento-2.3.2-p2.js +8 -2
  41. package/lib/config/versions/magento-2.3.2.js +8 -2
  42. package/lib/config/versions/magento-2.3.3-p1.js +8 -2
  43. package/lib/config/versions/magento-2.3.3.js +8 -2
  44. package/lib/config/versions/magento-2.3.4-p1.js +44 -0
  45. package/lib/config/versions/magento-2.3.4-p2.js +8 -2
  46. package/lib/config/versions/magento-2.3.4.js +8 -2
  47. package/lib/config/versions/magento-2.3.5-p1.js +8 -2
  48. package/lib/config/versions/magento-2.3.5-p2.js +8 -2
  49. package/lib/config/versions/magento-2.3.5.js +8 -2
  50. package/lib/config/versions/magento-2.3.6-p1.js +8 -2
  51. package/lib/config/versions/magento-2.3.6.js +8 -2
  52. package/lib/config/versions/magento-2.3.7-p1.js +8 -2
  53. package/lib/config/versions/magento-2.3.7-p2.js +8 -2
  54. package/lib/config/versions/magento-2.3.7-p3.js +8 -2
  55. package/lib/config/versions/magento-2.3.7.js +8 -2
  56. package/lib/config/versions/magento-2.4.0-p1.js +9 -3
  57. package/lib/config/versions/magento-2.4.0.js +9 -3
  58. package/lib/config/versions/magento-2.4.1-p1.js +6 -5
  59. package/lib/config/versions/magento-2.4.1.js +9 -2
  60. package/lib/config/versions/magento-2.4.2-p1.js +6 -5
  61. package/lib/config/versions/magento-2.4.2-p2.js +6 -6
  62. package/lib/config/versions/magento-2.4.2.js +6 -6
  63. package/lib/config/versions/magento-2.4.3-p1.js +7 -7
  64. package/lib/config/versions/magento-2.4.3-p2.js +7 -7
  65. package/lib/config/versions/magento-2.4.3.js +7 -7
  66. package/lib/config/versions/magento-2.4.4.js +9 -2
  67. package/lib/tasks/cleanup.js +2 -2
  68. package/lib/tasks/composer/index.js +14 -104
  69. package/lib/tasks/docker/api.d.ts +71 -0
  70. package/lib/tasks/docker/api.js +30 -0
  71. package/lib/tasks/docker/containers/container-api.d.ts +128 -0
  72. package/lib/tasks/docker/containers/container-api.js +153 -0
  73. package/lib/tasks/docker/containers/index.js +19 -0
  74. package/lib/tasks/docker/containers/tasks.js +215 -0
  75. package/lib/tasks/docker/convert-legacy-volumes.js +7 -3
  76. package/lib/tasks/docker/image/image-api.d.ts +34 -0
  77. package/lib/tasks/docker/image/image-api.js +49 -0
  78. package/lib/tasks/docker/image/index.js +5 -0
  79. package/lib/tasks/docker/index.js +1 -13
  80. package/lib/tasks/docker/network/index.js +7 -0
  81. package/lib/tasks/docker/network/network-api.d.ts +103 -0
  82. package/lib/tasks/docker/network/network-api.js +99 -0
  83. package/lib/tasks/docker/{network.js → network/tasks.js} +17 -10
  84. package/lib/tasks/docker/project-image-builder.js +167 -0
  85. package/lib/tasks/docker/volumes.js +1 -1
  86. package/lib/tasks/file-system/create-nginx-config.js +2 -3
  87. package/lib/tasks/file-system/create-php-config.js +20 -5
  88. package/lib/tasks/file-system/create-php-fpm-config.js +8 -4
  89. package/lib/tasks/file-system/create-phpstorm-config/exclude-folder-config.js +41 -29
  90. package/lib/tasks/file-system/create-phpstorm-config/index.js +2 -0
  91. package/lib/tasks/file-system/create-phpstorm-config/keys.js +15 -1
  92. package/lib/tasks/file-system/create-phpstorm-config/php-config/index.js +4 -1
  93. package/lib/tasks/file-system/create-phpstorm-config/php-config/php-config.js +3 -3
  94. package/lib/tasks/file-system/create-phpstorm-config/php-config/php-interpreters-config.js +81 -0
  95. package/lib/tasks/file-system/create-phpstorm-config/php-docker-settings-config/index.js +42 -0
  96. package/lib/tasks/file-system/create-phpstorm-config/php-docker-settings-config/php-docker-settings-config.js +9 -0
  97. package/lib/tasks/file-system/create-phpstorm-config/php-docker-settings-config/setup-php-docker-container-settings-config.js +66 -0
  98. package/lib/tasks/file-system/create-phpstorm-config/setup-xml-structure.js +11 -9
  99. package/lib/tasks/file-system/create-phpstorm-config/stylelint-config.js +2 -1
  100. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/composer-settings-config.js +15 -30
  101. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/index.js +24 -5
  102. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/mess-detector-project-configuration-config.js +50 -0
  103. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-code-sniffer-project-configuration-config.js +50 -0
  104. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-cs-fixer-project-configuration-config.js +50 -0
  105. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-debug-general-config.js +15 -15
  106. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-server-config.js +29 -3
  107. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-stan-project-configuration-config.js.js +50 -0
  108. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-workspace-project-configuration-config.js +42 -0
  109. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/psalm-project-configuration-config.js.js +50 -0
  110. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/workspace-config.js +0 -2
  111. package/lib/tasks/file-system/create-ssl-terminator-config.js +4 -2
  112. package/lib/tasks/file-system/create-vscode-config.js +57 -42
  113. package/lib/tasks/file-system/index.js +2 -1
  114. package/lib/tasks/link.js +1 -2
  115. package/lib/tasks/magento/install-magento-project.js +39 -64
  116. package/lib/tasks/magento/setup-magento/adjust-full-page-cache.js +4 -4
  117. package/lib/tasks/magento/setup-magento/configure-elasticsearch.js +6 -2
  118. package/lib/tasks/magento/setup-magento/create-admin.js +5 -6
  119. package/lib/tasks/magento/setup-magento/disable-2fa.js +3 -5
  120. package/lib/tasks/magento/setup-magento/disable-maintenance-mode.js +3 -4
  121. package/lib/tasks/magento/setup-magento/index.js +1 -3
  122. package/lib/tasks/magento/setup-magento/install-magento.js +57 -11
  123. package/lib/tasks/magento/setup-magento/migrate-database.js +12 -11
  124. package/lib/tasks/magento/setup-magento/set-deployment-mode.js +4 -4
  125. package/lib/tasks/magento/setup-magento/varnish-config.js +7 -5
  126. package/lib/tasks/magento/setup-magento/waiting-for-varnish.js +128 -3
  127. package/lib/tasks/mysql/connect-to-mysql.js +44 -19
  128. package/lib/tasks/mysql/import-dump-to-mysql.js +161 -25
  129. package/lib/tasks/php/php-container.js +108 -0
  130. package/lib/tasks/php/update-env-php.js +35 -19
  131. package/lib/tasks/php/update-env.php +61 -56
  132. package/lib/tasks/php/working_health_check.php +85 -0
  133. package/lib/tasks/requirements/{composer.js → composer-credentials.js} +3 -1
  134. package/lib/tasks/requirements/docker/index.js +15 -12
  135. package/lib/tasks/requirements/docker/performance.js +54 -0
  136. package/lib/tasks/requirements/docker/version.js +8 -8
  137. package/lib/tasks/requirements/index.js +7 -10
  138. package/lib/tasks/requirements/php-version.js +8 -116
  139. package/lib/tasks/requirements/platform.js +12 -25
  140. package/lib/tasks/start.js +41 -48
  141. package/lib/tasks/status/index.js +4 -10
  142. package/lib/tasks/stop.js +0 -2
  143. package/lib/tasks/theme/install-theme.js +1 -1
  144. package/lib/tasks/theme/symlink-theme.js +2 -3
  145. package/lib/util/config-php-json.js +13 -4
  146. package/lib/util/dockerfile-builder/build-instructions.js +197 -0
  147. package/lib/util/dockerfile-builder/index.js +276 -0
  148. package/lib/util/dockerfile-builder/types.d.ts +123 -0
  149. package/lib/util/env-php-json.js +10 -6
  150. package/lib/util/exec-async-command.js +3 -9
  151. package/lib/util/macos-version.js +20 -0
  152. package/lib/util/magento-task.js +3 -4
  153. package/lib/util/nginx-logs-parser.js +122 -0
  154. package/lib/util/php-task.js +3 -6
  155. package/lib/util/prefix.js +1 -1
  156. package/lib/util/run-composer.js +6 -14
  157. package/lib/util/run-container-image.js +17 -0
  158. package/lib/util/run-magento.js +6 -14
  159. package/lib/util/run-php.js +6 -17
  160. package/package.json +9 -9
  161. package/typings/context.d.ts +3 -9
  162. package/typings/index.d.ts +38 -57
  163. package/lib/config/composer.js +0 -11
  164. package/lib/config/php/extensions/libsodium.js +0 -36
  165. package/lib/config/php/releases/php-7.2.js +0 -25
  166. package/lib/config/php/releases/php-7.3.js +0 -25
  167. package/lib/config/php/releases/php-7.4.js +0 -23
  168. package/lib/config/php/releases/php-8.1.js +0 -25
  169. package/lib/config/phpbrew.js +0 -12
  170. package/lib/tasks/composer/install-prestissimo.js +0 -88
  171. package/lib/tasks/docker/containers.js +0 -231
  172. package/lib/tasks/php/bundled-extensions.js +0 -27
  173. package/lib/tasks/php/compile-options.js +0 -56
  174. package/lib/tasks/php/compile.js +0 -55
  175. package/lib/tasks/php/configure.js +0 -89
  176. package/lib/tasks/php/extensions/disable.js +0 -49
  177. package/lib/tasks/php/extensions/enable.js +0 -52
  178. package/lib/tasks/php/extensions/index.js +0 -80
  179. package/lib/tasks/php/extensions/install.js +0 -54
  180. package/lib/tasks/php/index.js +0 -79
  181. package/lib/tasks/php/install-sodium.js +0 -93
  182. package/lib/tasks/php/update-phpbrew.js +0 -45
  183. package/lib/tasks/php/validate-php.js +0 -67
  184. package/lib/tasks/php-fpm/get-process-id.js +0 -14
  185. package/lib/tasks/php-fpm/index.js +0 -4
  186. package/lib/tasks/php-fpm/start-php-fpm.js +0 -49
  187. package/lib/tasks/php-fpm/stop-php-fpm.js +0 -48
  188. package/lib/tasks/requirements/phpbrew/index.js +0 -61
  189. package/lib/tasks/requirements/phpbrew/install.js +0 -159
  190. package/lib/tasks/requirements/phpbrew/version.js +0 -28
@@ -1,14 +1,48 @@
1
+ const logger = require('@scandipwa/scandipwa-dev-utils/logger');
2
+ const semver = require('semver');
3
+ const fs = require('fs');
4
+ const path = require('path');
1
5
  const { request } = require('smol-request');
2
6
  const KnownError = require('../../../errors/known-error');
7
+ const { execAsyncSpawn } = require('../../../util/exec-async-command');
8
+ const { NginxParser } = require('../../../util/nginx-logs-parser');
3
9
  const sleep = require('../../../util/sleep');
4
10
 
11
+ const pathToWorkingHealthCheckPhp = path.join(__dirname, '..', '..', 'php', 'working_health_check.php');
12
+ const pathToProjectsHealthCheckPhp = path.join(process.cwd(), 'pub', 'health_check.php');
13
+ const pathToHealthCheckBackupPhp = path.join(process.cwd(), 'pub', 'health_check-super-backup-file.php');
14
+
15
+ /**
16
+ * @param {import('../../../../typings/context').ListrContext} ctx
17
+ */
18
+ const getIsHealthCheckRequestBroken = async (ctx) => {
19
+ const { nginx } = ctx.config.docker.getContainers(ctx.ports);
20
+
21
+ const nginxLogs = await execAsyncSpawn(`docker logs ${nginx.name}`);
22
+
23
+ const healthCheckRequests = nginxLogs
24
+ .split('\n')
25
+ .filter((line) => line.includes('"GET /health_check.php HTTP/1.1"'));
26
+
27
+ // eslint-disable-next-line max-len
28
+ const parser = new NginxParser('$remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"');
29
+
30
+ const parsedRequests = healthCheckRequests.map((line) => parser.parseLine(line));
31
+
32
+ return parsedRequests.every((parsedRequest) => parsedRequest.status !== '200');
33
+ };
34
+
5
35
  /**
6
36
  * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
7
37
  */
8
38
  const waitingForVarnish = () => ({
9
39
  title: 'Waiting for Varnish to return code 200',
10
- skip: (ctx) => !ctx.config.overridenConfiguration.configuration.varnish.enabled,
11
- task: async (ctx) => {
40
+ skip: (ctx) => ctx.debug || !ctx.config.overridenConfiguration.configuration.varnish.enabled,
41
+ task: async (ctx, task) => {
42
+ const pureMagentoVersion = ctx.magentoVersion.match(/^([0-9]+\.[0-9]+\.[0-9]+)/)[1];
43
+
44
+ const isMagento23 = semver.satisfies(pureMagentoVersion, '<2.4');
45
+
12
46
  let tries = 0;
13
47
  while (tries < 10) {
14
48
  try {
@@ -28,6 +62,94 @@ const waitingForVarnish = () => ({
28
62
  }
29
63
  }
30
64
 
65
+ if (tries === 10 && isMagento23) {
66
+ const isHealthCheckRequestBroken = await getIsHealthCheckRequestBroken(ctx);
67
+ if (isHealthCheckRequestBroken) {
68
+ const confirm = await task.prompt({
69
+ type: 'Select',
70
+ message: `We detected that your Magento instance is experiencing problems with Varnish server.
71
+ Health Check request is returning status 500.
72
+
73
+ Do you want to try resolving this issue by replacing ${logger.style.file('./pub/health_check.php')} file content with content from newer Magento version (2.4.4)?`,
74
+ choices: [
75
+ {
76
+ name: 'yes',
77
+ message: 'Okay, let\'s try that'
78
+ },
79
+ {
80
+ name: 'no',
81
+ message: 'I will try to fix this myself, thanks for the info!'
82
+ }
83
+ ]
84
+ });
85
+
86
+ if (confirm === 'yes') {
87
+ await fs.promises.writeFile(
88
+ pathToHealthCheckBackupPhp,
89
+ await fs.promises.readFile(pathToProjectsHealthCheckPhp, 'utf-8'),
90
+ 'utf-8'
91
+ );
92
+
93
+ task.output = `Backup made! Available at ${logger.style.file(pathToHealthCheckBackupPhp)}`;
94
+
95
+ await fs.promises.writeFile(
96
+ pathToProjectsHealthCheckPhp,
97
+ await fs.promises.readFile(pathToWorkingHealthCheckPhp, 'utf-8'),
98
+ 'utf-8'
99
+ );
100
+
101
+ task.output = `${logger.style.file('./pub/health_check.php')} content is replaced, waiting for nginx logs...`;
102
+
103
+ let connectionFixed = false;
104
+
105
+ for (let i = 0; i < 10; i++) {
106
+ try {
107
+ const response = await request(`http://localhost:${ctx.ports.sslTerminator}/`, {
108
+ responseType: 'headers'
109
+ });
110
+
111
+ if (response.status !== 200) {
112
+ await sleep(2000);
113
+ } else {
114
+ connectionFixed = true;
115
+ break;
116
+ }
117
+ } catch (e) {
118
+ await sleep(200);
119
+ }
120
+ }
121
+
122
+ if (connectionFixed) {
123
+ task.output = 'Connection looks good!';
124
+ const secondConfirm = await task.prompt({
125
+ type: 'Select',
126
+ message: `Okay, looks like it helped!
127
+ Do you want to keep backed up ${logger.style.file('health_check.php')} file?`,
128
+ choices: [
129
+ {
130
+ name: 'no',
131
+ message: 'I don\'t think I will need it anymore, thanks'
132
+ },
133
+ {
134
+ name: 'yes',
135
+ message: 'I will keep it, maybe it will be useful (X to doubt)'
136
+ }
137
+ ]
138
+ });
139
+
140
+ if (secondConfirm === 'no') {
141
+ await fs.promises.rm(pathToHealthCheckBackupPhp);
142
+ }
143
+ }
144
+
145
+ return;
146
+ }
147
+
148
+ task.skip('User choose to fix issue himself');
149
+ return;
150
+ }
151
+ }
152
+
31
153
  if (tries === 10) {
32
154
  throw new KnownError(`After 20 seconds website is still responding with non-200 code, which might indicate issue with setup.
33
155
  Or Varnish is still loading...
@@ -35,7 +157,10 @@ Or Varnish is still loading...
35
157
  Please check the logs!`);
36
158
  }
37
159
  },
38
- exitOnError: false
160
+ exitOnError: false,
161
+ options: {
162
+ bottomBar: 10
163
+ }
39
164
  });
40
165
 
41
166
  module.exports = waitingForVarnish;
@@ -1,4 +1,4 @@
1
- const mysql = require('mysql2/promise');
1
+ const mysql2 = require('mysql2/promise');
2
2
  const UnknownError = require('../../errors/unknown-error');
3
3
  const { execAsyncSpawn } = require('../../util/exec-async-command');
4
4
  const sleep = require('../../util/sleep');
@@ -10,10 +10,14 @@ const { createMagentoDatabase } = require('./create-magento-database');
10
10
  const waitForMySQLInitialization = () => ({
11
11
  title: 'Waiting for MySQL to initialize',
12
12
  task: async (ctx, task) => {
13
- const { mysql: { name } } = ctx.config.docker.getContainers();
13
+ const { mysql } = ctx.config.docker.getContainers();
14
+
15
+ task.title = `Waiting for ${mysql._} to initialize`;
16
+
14
17
  let mysqlReadyForConnections = false;
18
+
15
19
  while (!mysqlReadyForConnections) {
16
- const mysqlOutput = await execAsyncSpawn(`docker logs ${name}`);
20
+ const mysqlOutput = await execAsyncSpawn(`docker logs ${mysql.name}`);
17
21
  if (mysqlOutput.includes('ready for connections')) {
18
22
  mysqlReadyForConnections = true;
19
23
  break;
@@ -23,7 +27,7 @@ Please wait, this will take some time and do not restart the MySQL container unt
23
27
 
24
28
  let mysqlFinishedInitialization = false;
25
29
  while (!mysqlFinishedInitialization) {
26
- const mysqlOutput = await execAsyncSpawn(`docker logs ${name}`);
30
+ const mysqlOutput = await execAsyncSpawn(`docker logs ${mysql.name}`);
27
31
  if (mysqlOutput.includes('init process done.') && !mysqlFinishedInitialization) {
28
32
  mysqlFinishedInitialization = true;
29
33
  break;
@@ -47,19 +51,22 @@ const gettingMySQLConnection = () => ({
47
51
  title: 'Getting MySQL connection',
48
52
  task: async (ctx, task) => {
49
53
  const { config: { docker }, ports } = ctx;
50
- const { mysql: { env } } = docker.getContainers();
54
+ const { mysql } = docker.getContainers(ctx.ports);
51
55
  let tries = 0;
52
56
  const maxTries = 20;
53
57
  const errors = [];
58
+
59
+ task.title = `Getting ${mysql._} connection`;
60
+
54
61
  while (tries < maxTries) {
55
62
  tries++;
56
63
  try {
57
- const connection = await mysql.createConnection({
64
+ const connection = await mysql2.createConnection({
58
65
  host: '127.0.0.1',
59
66
  port: ports.mysql,
60
- user: env.MYSQL_USER,
61
- password: env.MYSQL_PASSWORD,
62
- database: env.MYSQL_DATABASE
67
+ user: mysql.env.MYSQL_USER,
68
+ password: mysql.env.MYSQL_PASSWORD,
69
+ database: mysql.env.MYSQL_DATABASE
63
70
  });
64
71
 
65
72
  ctx.mysqlConnection = connection;
@@ -78,22 +85,40 @@ const gettingMySQLConnection = () => ({
78
85
  }
79
86
  });
80
87
 
88
+ /**
89
+ * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
90
+ */
91
+ const terminatingExistingConnection = () => ({
92
+ title: 'Terminating existing Database connection',
93
+ skip: (ctx) => !ctx.mysqlConnection,
94
+ task: (ctx) => {
95
+ ctx.mysqlConnection.destroy();
96
+ }
97
+ });
98
+
81
99
  /**
82
100
  * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
83
101
  */
84
102
  const connectToMySQL = () => ({
85
103
  title: 'Connecting to MySQL server',
86
104
  skip: (ctx) => ctx.skipSetup,
87
- task: (ctx, task) => task.newListr([
88
- waitForMySQLInitialization(),
89
- createMagentoDatabase(),
90
- gettingMySQLConnection()
91
- ], {
92
- concurrent: false,
93
- rendererOptions: {
94
- collapse: true
95
- }
96
- }),
105
+ task: (ctx, task) => {
106
+ const { mysql } = ctx.config.docker.getContainers();
107
+
108
+ task.title = `Connecting to ${mysql._} server`;
109
+
110
+ return task.newListr([
111
+ waitForMySQLInitialization(),
112
+ createMagentoDatabase(),
113
+ terminatingExistingConnection(),
114
+ gettingMySQLConnection()
115
+ ], {
116
+ concurrent: false,
117
+ rendererOptions: {
118
+ collapse: true
119
+ }
120
+ });
121
+ },
97
122
  options: {
98
123
  bottomBar: 10
99
124
  }
@@ -2,23 +2,100 @@
2
2
  const logger = require('@scandipwa/scandipwa-dev-utils/logger');
3
3
  const KnownError = require('../../errors/known-error');
4
4
  const UnknownError = require('../../errors/unknown-error');
5
- const { execAsyncSpawn } = require('../../util/exec-async-command');
5
+ const { execAsyncSpawn, execCommandTask } = require('../../util/exec-async-command');
6
6
  const pathExists = require('../../util/path-exists');
7
+ const connectToMySQL = require('./connect-to-mysql');
7
8
 
8
9
  /**
9
10
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
10
11
  */
11
- const importDumpToMySQL = () => ({
12
- title: 'Importing Database Dump To MySQL',
12
+ const copyDatabaseDumpIntoContainer = () => ({
13
+ title: 'Copying database dump into container',
13
14
  task: async (ctx, task) => {
14
- if (!await pathExists(ctx.importDb)) {
15
- throw new KnownError(`Dump file at ${ctx.importDb} does not exist. Please provide correct relative path to the file`);
16
- }
15
+ const { config: { docker }, ports } = ctx;
16
+ const { mysql } = docker.getContainers(ports);
17
+
18
+ return task.newListr(
19
+ execCommandTask(`docker cp ${ctx.importDb} ${mysql.name}:/dump.sql`, {
20
+ logOutput: true
21
+ })
22
+ );
23
+ }
24
+ });
17
25
 
26
+ /**
27
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
28
+ */
29
+ const runSetGlobalLogBinTrustFunctionCreatorsCommand = () => ({
30
+ task: async (ctx, task) => {
18
31
  const { config: { docker }, ports } = ctx;
32
+ const { mysql } = docker.getContainers(ports);
19
33
 
34
+ return task.newListr(
35
+ execCommandTask(`docker exec ${mysql.name} bash -c 'mysql -uroot -p${mysql.env.MYSQL_ROOT_PASSWORD} -e "SET GLOBAL log_bin_trust_function_creators = 1;"'`)
36
+ );
37
+ }
38
+ });
39
+
40
+ /**
41
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
42
+ */
43
+ const deleteDatabaseBeforeImportingDumpPrompt = () => ({
44
+ title: 'Deleting magento database before importing dump',
45
+ task: async (ctx, task) => {
46
+ const deleteDatabaseMagentoChoice = await task.prompt({
47
+ type: 'Select',
48
+ message: `Before importing database dump, would you like to delete existing database?
49
+
50
+ It is possible that dump might interfere with existing data in database.
51
+
52
+ Note that you will lose your existing database!`,
53
+ choices: [
54
+ {
55
+ name: 'delete',
56
+ message: 'YES I AM SURE I WANT TO DELETE magento DATABASE!'
57
+ },
58
+ {
59
+ name: 'skip',
60
+ message: 'Skip this step'
61
+ }
62
+ ]
63
+ });
64
+
65
+ if (deleteDatabaseMagentoChoice === 'delete') {
66
+ await ctx.mysqlConnection.query('DROP DATABASE IF EXISTS magento;');
67
+ await ctx.mysqlConnection.query('CREATE DATABASE magento;');
68
+ return;
69
+ }
70
+ task.skip();
71
+ }
72
+ });
73
+
74
+ /**
75
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
76
+ */
77
+ const executeImportDumpSQL = () => ({
78
+ task: async (ctx, task) => {
79
+ const { config: { docker }, ports } = ctx;
20
80
  const { mysql } = docker.getContainers(ports);
21
81
 
82
+ const userCredentialsForMySQLCLI = await task.prompt({
83
+ type: 'Select',
84
+ message: 'Which user do you want to use to import db in MySQL client?',
85
+ choices: [
86
+ {
87
+ name: `--user=root --password=${mysql.env.MYSQL_ROOT_PASSWORD}`,
88
+ message: `root (${logger.style.command('Probably safest option')})`
89
+ },
90
+ {
91
+ name: `--user=${mysql.env.MYSQL_USER} --password=${mysql.env.MYSQL_PASSWORD}`,
92
+ message: `${mysql.env.MYSQL_USER}`
93
+ }
94
+ ]
95
+ });
96
+
97
+ const importCommand = `docker exec ${mysql.name} bash -c "mysql ${userCredentialsForMySQLCLI} magento < ./dump.sql"`;
98
+
22
99
  const startImportTime = Date.now();
23
100
  const tickInterval = setInterval(() => {
24
101
  task.title = `Importing Database Dump To MySQL, ${Math.floor((Date.now() - startImportTime) / 1000)}s in progress...`;
@@ -26,18 +103,7 @@ const importDumpToMySQL = () => ({
26
103
 
27
104
  try {
28
105
  await execAsyncSpawn(
29
- `docker cp ${ctx.importDb} ${mysql.name}:/dump.sql`
30
- );
31
-
32
- await execAsyncSpawn(
33
- `docker exec ${mysql.name} bash -c 'mysql -uroot -p${mysql.env.MYSQL_ROOT_PASSWORD} -e "SET GLOBAL log_bin_trust_function_creators = 1;"'`
34
- );
35
-
36
- /**
37
- * Using `mysql` instead of `mysqlimport` because `mysqlimport` has permission issues during import
38
- */
39
- await execAsyncSpawn(
40
- `docker exec ${mysql.name} bash -c "mysql -umagento -pmagento magento < ./dump.sql"`,
106
+ importCommand,
41
107
  {
42
108
  callback: (t) => {
43
109
  task.output = t;
@@ -45,24 +111,94 @@ const importDumpToMySQL = () => ({
45
111
  }
46
112
  );
47
113
  } catch (e) {
48
- if (e.includes('Unknown collation: \'utf8mb4_0900_ai_ci\'')) {
49
- throw new KnownError(`Error happened during database dump import!
114
+ if (e.message.includes('Unknown collation: \'utf8mb4_0900_ai_ci\'')) {
115
+ const confirmFixingCollation = await task.prompt({
116
+ type: 'Select',
117
+ message: `We got the following error while trying to import ${logger.style.file('dump.sql')}!
118
+
119
+ ${e.message}
120
+
121
+ To fix this error we suggest running the following commands:
122
+ ${logger.style.command('sed -i \'s/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g\' dump.sql')}
123
+ `,
124
+ choices: [
125
+ {
126
+ name: 'yes',
127
+ message: 'Yes, run the following commands, I reaaaalllyy want dump to work! (this will not edit original dump.sql)'
128
+ },
129
+ {
130
+ name: 'no',
131
+ message: 'Okay, I got it. Will try to fix myself'
132
+ }
133
+ ]
134
+ });
135
+
136
+ if (confirmFixingCollation === 'yes') {
137
+ task.output = 'Running fix command...';
138
+ await execAsyncSpawn(`docker exec ${mysql.name} bash -c "sed -i 's/utf8mb4_0900_ai_ci/utf8mb4_general_ci/g' dump.sql"`);
50
139
 
51
- ${e}
140
+ task.output = 'Trying to import dump again...';
141
+ try {
142
+ await execAsyncSpawn(
143
+ importCommand,
144
+ {
145
+ callback: (t) => {
146
+ task.output = t;
147
+ }
148
+ }
149
+ );
150
+
151
+ return;
152
+ } catch (e) {
153
+ throw new KnownError(`Fixing ${logger.style.file('dump.sql')} collations did not helped, we got the following error:
154
+ ${e.message}`);
155
+ }
156
+ } else {
157
+ throw new KnownError(`Database dump import unsuccessful!
158
+
159
+ ${e.message}
52
160
 
53
161
  You can try replacing all occurrences of ${logger.style.misc('utf8mb4_0900_ai_ci')} with ${logger.style.misc('utf8mb4_general_ci')} in your ${logger.style.file(ctx.importDb)} file!`);
162
+ }
54
163
  }
55
164
 
56
165
  throw new UnknownError(`Unexpected error during dump import.\n\n${e}`);
166
+ } finally {
167
+ clearInterval(tickInterval);
57
168
  }
58
-
59
- clearInterval(tickInterval);
60
-
61
- task.title = 'Database imported!';
62
169
  },
63
170
  options: {
64
171
  bottomBar: 10
65
172
  }
66
173
  });
67
174
 
175
+ /**
176
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
177
+ */
178
+ const importDumpToMySQL = () => ({
179
+ title: 'Importing Database Dump To MySQL',
180
+ task: async (ctx, task) => {
181
+ if (!await pathExists(ctx.importDb)) {
182
+ throw new KnownError(`Dump file at ${ctx.importDb} does not exist. Please provide correct relative path to the file`);
183
+ }
184
+
185
+ return task.newListr([
186
+ copyDatabaseDumpIntoContainer(),
187
+ deleteDatabaseBeforeImportingDumpPrompt(),
188
+ runSetGlobalLogBinTrustFunctionCreatorsCommand(),
189
+ executeImportDumpSQL(),
190
+ connectToMySQL(),
191
+ {
192
+ task: () => {
193
+ task.title = 'Database imported!';
194
+ }
195
+ }
196
+ ], {
197
+ rendererOptions: {
198
+ collapse: false
199
+ }
200
+ });
201
+ }
202
+ });
203
+
68
204
  module.exports = importDumpToMySQL;
@@ -0,0 +1,108 @@
1
+ /* eslint-disable no-use-before-define */
2
+ const { deepmerge } = require('../../util/deepmerge');
3
+ const { containerApi } = require('../docker/containers');
4
+
5
+ /**
6
+ * @param {import('../../../typings/context').ListrContext} ctx
7
+ * @param {import('../../util/exec-async-command').ExecAsyncSpawnOptions<false> & { useXDebugContainer?: boolean }} [options]
8
+ * @param {string} command
9
+ */
10
+ const runPHPContainerCommand = async (ctx, command, options = {}) => {
11
+ const { php } = ctx.config.docker.getContainers(ctx.ports);
12
+
13
+ const containers = await containerApi.ls({ formatToJSON: true, all: true });
14
+
15
+ if (containers.some((c) => c.Names === php.name)) {
16
+ return execPHPContainerCommand(ctx, command, options);
17
+ }
18
+
19
+ return containerApi.run(
20
+ deepmerge(
21
+ php,
22
+ {
23
+ detach: false,
24
+ rm: true
25
+ },
26
+ options.useXDebugContainer
27
+ ? {
28
+ image: `${ php.image }.xdebug`
29
+ }
30
+ : {},
31
+ {
32
+ command
33
+ },
34
+ ),
35
+ options
36
+ );
37
+ };
38
+
39
+ /**
40
+ * @param {string} command
41
+ * @param {import('../../util/exec-async-command').ExecAsyncSpawnOptions<false> & { useXDebugContainer?: boolean }} [options]
42
+ * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
43
+ */
44
+ const runPHPContainerCommandTask = (command, options = {}) => ({
45
+ title: `Running command "${command}"`,
46
+ task: (ctx, task) => runPHPContainerCommand(ctx, command, {
47
+ callback: !ctx.verbose ? undefined : (t) => {
48
+ task.output = t;
49
+ },
50
+ ...options
51
+ }),
52
+ option: {
53
+ bottomBar: 10
54
+ }
55
+ });
56
+
57
+ /**
58
+ *
59
+ * @param {import('../../../typings/context').ListrContext} ctx
60
+ * @param {import('../../util/exec-async-command').ExecAsyncSpawnOptions<false> & { useXDebugContainer?: boolean }} [options]
61
+ * @param {string} command
62
+ */
63
+ const execPHPContainerCommand = async (ctx, command, options = {}) => {
64
+ const { php } = ctx.config.docker.getContainers(ctx.ports);
65
+
66
+ const containers = await containerApi.ls({ formatToJSON: true, all: true });
67
+
68
+ if (!containers.some((c) => c.Names === php.name)) {
69
+ return runPHPContainerCommand(ctx, command, options);
70
+ }
71
+
72
+ return containerApi.exec(
73
+ command,
74
+ php.name,
75
+ deepmerge(
76
+ php,
77
+ options.env ? {
78
+ env: options.env
79
+ } : {}
80
+ ),
81
+ options
82
+ );
83
+ };
84
+
85
+ /**
86
+ * @param {string} command
87
+ * @param {import('../../util/exec-async-command').ExecAsyncSpawnOptions<false> & { useXDebugContainer?: boolean, title?: string }} [options]
88
+ * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
89
+ */
90
+ const execPHPContainerCommandTask = (command, options = {}) => ({
91
+ title: typeof options.title === 'string' && options.title === '' ? undefined : options.title || `Running command "${command}"`,
92
+ task: (ctx, task) => execPHPContainerCommand(ctx, command, {
93
+ callback: !ctx.verbose ? undefined : (t) => {
94
+ task.output = t;
95
+ },
96
+ ...options
97
+ }),
98
+ option: {
99
+ bottomBar: 10
100
+ }
101
+ });
102
+
103
+ module.exports = {
104
+ runPHPContainerCommand,
105
+ runPHPContainerCommandTask,
106
+ execPHPContainerCommand,
107
+ execPHPContainerCommandTask
108
+ };