@scandipwa/magento-scripts 2.4.0-alpha.0 → 2.4.0-alpha.2

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 (100) hide show
  1. package/exec.js +2 -2
  2. package/index.js +1 -0
  3. package/lib/commands/cli.js +17 -19
  4. package/lib/commands/execute.js +8 -4
  5. package/lib/commands/start.js +0 -6
  6. package/lib/config/docker.js +196 -79
  7. package/lib/config/get-project-configuration.js +0 -5
  8. package/lib/config/php-config.js +4 -0
  9. package/lib/config/port-config.js +47 -10
  10. package/lib/config/services/composer/versions/composer-2.8.js +1 -1
  11. package/lib/config/services/elasticsearch/default-es-env.js +1 -1
  12. package/lib/config/services/elasticsearch/versions/elasticsearch-8.17.js +14 -0
  13. package/lib/config/services/elasticsearch/versions/index.js +3 -1
  14. package/lib/config/services/mariadb/versions/index.js +3 -1
  15. package/lib/config/services/mariadb/versions/mariadb-10.2.js +3 -1
  16. package/lib/config/services/mariadb/versions/mariadb-10.3.js +3 -1
  17. package/lib/config/services/mariadb/versions/mariadb-10.4.js +3 -1
  18. package/lib/config/services/mariadb/versions/mariadb-10.6.js +3 -1
  19. package/lib/config/services/mariadb/versions/mariadb-11.4.js +11 -0
  20. package/lib/config/services/mariadb/versions/mariadb-11.6.js +11 -0
  21. package/lib/config/services/opensearch/default-os-env.js +1 -1
  22. package/lib/config/services/opensearch/versions/index.js +3 -1
  23. package/lib/config/services/opensearch/versions/opensearch-2.19.js +14 -0
  24. package/lib/config/services/php/extensions/ftp.js +7 -0
  25. package/lib/config/services/php/extensions/xdebug.js +1 -0
  26. package/lib/config/services/php/versions/index.js +2 -1
  27. package/lib/config/services/php/versions/php-7.2.js +0 -1
  28. package/lib/config/services/php/versions/php-7.3.js +0 -1
  29. package/lib/config/services/php/versions/php-7.4.js +0 -1
  30. package/lib/config/services/php/versions/php-8.1.js +4 -2
  31. package/lib/config/services/php/versions/php-8.2.js +1 -2
  32. package/lib/config/services/php/versions/php-8.3.js +32 -0
  33. package/lib/config/services/redis/index.js +6 -1
  34. package/lib/config/services/redis/valkey-8.0.js +8 -0
  35. package/lib/config/services/redis/valkey-8.1.js +8 -0
  36. package/lib/config/services/varnish/varnish-6-0.js +1 -1
  37. package/lib/config/services/varnish/varnish-6-6.js +1 -1
  38. package/lib/config/services/varnish/varnish-7-0.js +1 -1
  39. package/lib/config/services/varnish/varnish-7-1.js +1 -1
  40. package/lib/config/services/varnish/varnish-7-3.js +1 -1
  41. package/lib/config/services/varnish/varnish-7-4.js +1 -1
  42. package/lib/config/services/varnish/varnish-7-5.js +1 -1
  43. package/lib/config/services/varnish/varnish-7-6.js +1 -1
  44. package/lib/config/templates/nginx.template.conf +32 -9
  45. package/lib/config/templates/php-fpm.template.conf +1 -1
  46. package/lib/config/templates/ssl-terminator.template.conf +3 -1
  47. package/lib/config/versions/magento-2.4.4-p13.js +40 -0
  48. package/lib/config/versions/magento-2.4.5-p12.js +40 -0
  49. package/lib/config/versions/magento-2.4.6-p10.js +40 -0
  50. package/lib/config/versions/magento-2.4.7-p4.js +0 -1
  51. package/lib/config/versions/magento-2.4.7-p5.js +41 -0
  52. package/lib/config/versions/magento-2.4.8.js +43 -0
  53. package/lib/tasks/database/create-magento-database.js +6 -4
  54. package/lib/tasks/database/import-remote-db/ssh/index.js +1 -1
  55. package/lib/tasks/database/import-remote-db/ssh/readymage.js +1 -1
  56. package/lib/tasks/database/import-remote-db/ssh/regular-server.js +1 -1
  57. package/lib/tasks/docker/containers/container-api.d.ts +14 -3
  58. package/lib/tasks/docker/containers/container-api.js +16 -9
  59. package/lib/tasks/docker/containers/tasks.js +99 -28
  60. package/lib/tasks/docker/convert-mysql-to-mariadb.js +14 -22
  61. package/lib/tasks/docker/project-image-builder.js +153 -91
  62. package/lib/tasks/docker/system/system-api.d.ts +66 -0
  63. package/lib/tasks/docker/system/system-api.js +28 -1
  64. package/lib/tasks/execute.js +10 -10
  65. package/lib/tasks/file-system/create-nginx-config.js +22 -8
  66. package/lib/tasks/file-system/create-php-debug-config.js +1 -2
  67. package/lib/tasks/file-system/create-php-fpm-debug-config.js +33 -0
  68. package/lib/tasks/file-system/create-phpstorm-config/php-config/php-interpreters-config.js +5 -5
  69. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/composer-settings-config.js +2 -2
  70. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-workspace-project-configuration-config.js +4 -3
  71. package/lib/tasks/file-system/create-ssl-terminator-config.js +46 -7
  72. package/lib/tasks/file-system/index.js +2 -0
  73. package/lib/tasks/magento/install-magento-project.js +40 -24
  74. package/lib/tasks/magento/setup-magento/check-file-permissions.php +32 -0
  75. package/lib/tasks/magento/setup-magento/index.js +2 -0
  76. package/lib/tasks/magento/setup-magento/make-magento-binaries-executable.js +44 -0
  77. package/lib/tasks/magento/setup-magento/setup-file-permissions.js +160 -0
  78. package/lib/tasks/magento/setup-magento/waiting-for-varnish.js +0 -1
  79. package/lib/tasks/php/php-container.d.ts +3 -3
  80. package/lib/tasks/php/php-container.js +22 -18
  81. package/lib/tasks/php/update-env-php.js +7 -14
  82. package/lib/tasks/project-config/index.js +0 -3
  83. package/lib/tasks/requirements/cgroup-version.js +69 -0
  84. package/lib/tasks/requirements/elasticsearch-version.js +23 -7
  85. package/lib/tasks/requirements/index.js +3 -0
  86. package/lib/tasks/requirements/opensearch-version.js +5 -5
  87. package/lib/tasks/requirements/searchengine-version.js +1 -2
  88. package/lib/tasks/start.js +2 -13
  89. package/lib/util/dockerfile-builder/build-instructions.js +5 -1
  90. package/lib/util/dockerfile-builder/types.d.ts +1 -1
  91. package/lib/util/execute-in-container.js +14 -8
  92. package/lib/util/get-installed-magento-version.js +60 -2
  93. package/lib/util/portscanner.js +3 -3
  94. package/lib/util/run-composer.js +1 -1
  95. package/lib/util/run-magento.js +2 -1
  96. package/lib/util/run-php.js +2 -1
  97. package/lib/util/set-config.js +4 -2
  98. package/package.json +16 -16
  99. package/typings/context.d.ts +13 -5
  100. package/typings/index.d.ts +10 -5
@@ -0,0 +1,43 @@
1
+ const sodium = require('../services/php/extensions/sodium')
2
+ const ftp = require('../services/php/extensions/ftp')
3
+ const {
4
+ magento24PHPExtensionList
5
+ } = require('../magento/required-php-extensions')
6
+ const { php83 } = require('../services/php/versions')
7
+ const { sslTerminator } = require('../services/ssl-terminator')
8
+ const { varnish76 } = require('../services/varnish')
9
+ const { repo } = require('../services/php/base-repo')
10
+ const { nginx126 } = require('../services/nginx/versions')
11
+ const { composer28 } = require('../services/composer/versions')
12
+ const { maildev } = require('../services/maildev')
13
+ const { valkey80 } = require('../services/redis')
14
+ const { mariadb114 } = require('../services/mariadb/versions')
15
+ const { elasticsearch817 } = require('../services/elasticsearch/versions')
16
+ const { mysql80 } = require('../services/mysql/versions')
17
+ const { opensearch219 } = require('../services/opensearch/versions')
18
+
19
+ /**
20
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
21
+ */
22
+ module.exports = ({ templateDir }) => ({
23
+ magentoVersion: '2.4.8',
24
+ isDefault: true,
25
+ configuration: {
26
+ php: php83({
27
+ templateDir,
28
+ extensions: { ...magento24PHPExtensionList, sodium, ftp },
29
+ baseImage: `${repo}:php-8.3-magento-2.4`
30
+ }),
31
+ nginx: nginx126({ templateDir }),
32
+ redis: valkey80(),
33
+ mysql: mysql80(),
34
+ mariadb: mariadb114(),
35
+ elasticsearch: elasticsearch817(),
36
+ composer: composer28(),
37
+ varnish: varnish76({ templateDir }),
38
+ sslTerminator: sslTerminator({ templateDir }),
39
+ maildev: maildev(),
40
+ opensearch: opensearch219(),
41
+ searchengine: 'opensearch'
42
+ }
43
+ })
@@ -7,12 +7,14 @@ const { containerApi } = require('../docker/containers')
7
7
  const createMagentoDatabase = () => ({
8
8
  title: 'Creating Magento database',
9
9
  task: async (ctx, task) => {
10
+ const { configuration } = ctx.config.overridenConfiguration
10
11
  const { mariadb } = ctx.config.docker.getContainers()
11
12
  task.title = `Creating Magento database in ${mariadb._}`
12
- await containerApi.exec(
13
- `mysql -uroot -p${mariadb.env.MARIADB_ROOT_PASSWORD} -h 127.0.0.1 -e "CREATE DATABASE IF NOT EXISTS magento;"`,
14
- mariadb.name
15
- )
13
+
14
+ await containerApi.exec({
15
+ command: `${configuration.mariadb.binFileName} -uroot -p${mariadb.env.MARIADB_ROOT_PASSWORD} -h 127.0.0.1 -e "CREATE DATABASE IF NOT EXISTS magento;"`,
16
+ container: mariadb.name
17
+ })
16
18
  }
17
19
  })
18
20
 
@@ -1,5 +1,5 @@
1
1
  const os = require('os')
2
- const { NodeSSH } = require('node-ssh')
2
+ const { NodeSSH } = require('node-ssh-no-cpu-features')
3
3
  const pathExists = require('../../../../util/path-exists')
4
4
  const regularSSHServer = require('./regular-server')
5
5
  const readymageSSH = require('./readymage')
@@ -4,7 +4,7 @@ const { execAsyncSpawn } = require('../../../../util/exec-async-command')
4
4
  const databaseDumpCommandWithOptions = require('./database-dump-command')
5
5
 
6
6
  /**
7
- * @returns {import('listr2').ListrTask<import('../../../../../typings/context').ListrContext & { ssh: import('node-ssh').NodeSSH }>}
7
+ * @returns {import('listr2').ListrTask<import('../../../../../typings/context').ListrContext & { ssh: import('node-ssh-no-cpu-features').NodeSSH }>}
8
8
  */
9
9
  const readymageSSH = () => ({
10
10
  task: async (ctx, task) => {
@@ -4,7 +4,7 @@ const { execAsyncSpawn } = require('../../../../util/exec-async-command')
4
4
  const databaseDumpCommandWithOptions = require('./database-dump-command')
5
5
  const KnownError = require('../../../../errors/known-error')
6
6
  /**
7
- * @returns {import('listr2').ListrTask<import('../../../../../typings/context').ListrContext & { ssh: import('node-ssh').NodeSSH }>}
7
+ * @returns {import('listr2').ListrTask<import('../../../../../typings/context').ListrContext & { ssh: import('node-ssh-no-cpu-features').NodeSSH }>}
8
8
  */
9
9
  const regularSSHServer = () => ({
10
10
  task: async (ctx, task) => {
@@ -38,6 +38,12 @@ export function ls(
38
38
  ): Promise<ContainerLsResult[]>
39
39
 
40
40
  export interface ContainerExecOptions {
41
+ command: string
42
+
43
+ /**
44
+ * container id or name
45
+ */
46
+ container: string
41
47
  /**
42
48
  * Set environment variables [docs](https://docs.docker.com/engine/reference/commandline/run/#set-environment-variables--e---env---env-file)
43
49
  */
@@ -57,15 +63,20 @@ export interface ContainerExecOptions {
57
63
  * Allocate a pseudo-TTY
58
64
  */
59
65
  tty?: boolean
66
+
67
+ /**
68
+ * Keep STDIN open even if not attached
69
+ */
70
+ interactive?: boolean
60
71
  }
61
72
 
62
73
  export function exec<T>(
63
- command: string,
64
- container: string,
65
- options?: ContainerExecOptions,
74
+ options: ContainerExecOptions,
66
75
  execOptions?: ExecAsyncSpawnOptions<T>
67
76
  ): Promise<string>
68
77
 
78
+ export function execCommand(options: ContainerExecOptions): string[]
79
+
69
80
  export interface ContainerRunOptions {
70
81
  /**
71
82
  * Add a custom host-to-IP mapping (host:ip)
@@ -100,13 +100,10 @@ const run = (options, execOptions = {}) =>
100
100
  execAsyncSpawn(runCommand(options).join(' '), execOptions)
101
101
 
102
102
  /**
103
- * @param {string} command
104
- * @param {string} container container id or name
105
103
  * @param {import('./container-api').ContainerExecOptions} options
106
- * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions<false>} execOptions
107
104
  */
108
- const exec = (command, container, options = {}, execOptions = {}) => {
109
- const { env, tty, user, workdir } = options
105
+ const execCommand = (options) => {
106
+ const { command, container, env, tty, user, workdir, interactive } = options
110
107
  const envArgs = !env
111
108
  ? ''
112
109
  : Object.entries(env)
@@ -115,24 +112,33 @@ const exec = (command, container, options = {}, execOptions = {}) => {
115
112
  const ttyArg = tty ? '--tty' : ''
116
113
  const userArg = user ? `--user=${user}` : ''
117
114
  const workdirArg = workdir ? `--workdir=${workdir}` : ''
115
+ const interactiveArg = interactive ? '--interactive' : ''
118
116
 
119
- const execCommand = [
117
+ const dockerCommand = [
120
118
  'docker',
121
119
  'container',
122
120
  'exec',
123
121
  envArgs,
124
122
  ttyArg,
123
+ interactiveArg,
125
124
  userArg,
126
125
  workdirArg,
127
126
  container,
128
127
  command
129
128
  ]
130
- .filter(Boolean)
131
- .join(' ')
129
+ .flat()
130
+ .filter((arg) => !!arg && typeof arg === 'string')
132
131
 
133
- return execAsyncSpawn(execCommand, execOptions)
132
+ return dockerCommand
134
133
  }
135
134
 
135
+ /**
136
+ * @param {import('./container-api').ContainerExecOptions} options
137
+ * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions<false>} execOptions
138
+ */
139
+ const exec = (options, execOptions = {}) =>
140
+ execAsyncSpawn(execCommand(options).join(' '), execOptions)
141
+
136
142
  /**
137
143
  * @type {typeof import('./container-api')['ls']}
138
144
  */
@@ -243,6 +249,7 @@ module.exports = {
243
249
  run,
244
250
  runCommand,
245
251
  exec,
252
+ execCommand,
246
253
  ls,
247
254
  logs,
248
255
  stop,
@@ -34,6 +34,17 @@ const remoteImageReducer = (acc, val) => {
34
34
  return acc.concat([val.image])
35
35
  }
36
36
 
37
+ /**
38
+ * @param {{ pullImage?: boolean }} param0
39
+ */
40
+ const filterNonPullableImages = ({ pullImage }) => {
41
+ if (typeof pullImage === 'boolean' && !pullImage) {
42
+ return false
43
+ }
44
+
45
+ return true
46
+ }
47
+
37
48
  /**
38
49
  * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
39
50
  */
@@ -45,6 +56,7 @@ const pullImages = () => ({
45
56
  if (pullImages) {
46
57
  return task.newListr(
47
58
  containers
59
+ .filter(filterNonPullableImages)
48
60
  .reduce(remoteImageReducer, [])
49
61
  .map((image) => {
50
62
  const [repo, tag = 'latest'] = image.split(':')
@@ -77,6 +89,7 @@ const pullImages = () => ({
77
89
  }
78
90
 
79
91
  const imagesFilter = containers
92
+ .filter(filterNonPullableImages)
80
93
  .reduce(remoteImageReducer, [])
81
94
  .map((image) => `reference='${image}'`)
82
95
 
@@ -86,6 +99,7 @@ const pullImages = () => ({
86
99
  })
87
100
 
88
101
  const missingContainerImages = containers
102
+ .filter(filterNonPullableImages)
89
103
  .reduce(remoteImageReducer, [])
90
104
  .map((image) => {
91
105
  const [repo, tag = 'latest'] = image.split(':')
@@ -134,48 +148,105 @@ const pullImages = () => ({
134
148
  */
135
149
  const startContainers = () => ({
136
150
  title: 'Starting containers',
137
- task: async ({ ports, config: { docker }, debug }, task) => {
151
+ task: async ({ ports, config: { docker } }, task) => {
138
152
  const containerList = await containerApi.ls({
139
153
  formatToJSON: true,
140
154
  all: true
141
155
  })
142
156
 
143
- const missingContainers = Object.values(
144
- docker.getContainers(ports)
145
- ).filter(({ name }) => !containerList.some((c) => c.Names === name))
157
+ const missingContainers = Object.entries(docker.getContainers(ports))
158
+ .filter(
159
+ ([nameWithoutPrefix, { name }]) =>
160
+ !containerList.some((c) => c.Names === name)
161
+ )
162
+ .map(([nameWithoutPrefix, containerOptions]) => ({
163
+ ...containerOptions,
164
+ nameWithoutPrefix
165
+ }))
146
166
 
147
167
  if (missingContainers.length === 0) {
148
168
  task.skip()
149
169
  return
150
170
  }
151
171
 
152
- if (debug) {
153
- await Promise.all(
154
- missingContainers
155
- .map((container) => {
156
- if (container.debugImage) {
157
- container.image = container.debugImage
158
- }
172
+ const containerStatuses = missingContainers.reduce(
173
+ (acc, container) => ({
174
+ ...acc,
175
+ [container.nameWithoutPrefix]: {
176
+ started: false,
177
+ onStarted: []
178
+ }
179
+ }),
180
+ {}
181
+ )
159
182
 
160
- return container
161
- })
162
- .map((container) =>
163
- containerApi.run(container).then((out) => {
164
- task.output = `From ${container._}: ${out}`
165
- })
166
- )
167
- )
183
+ return task.newListr(
184
+ missingContainers.map((container) => ({
185
+ title: `Deploying ${logger.style.file(container._)} container`,
186
+ task: async (subCtx, subTask) => {
187
+ const { dependsOn } = container
188
+ if (Array.isArray(dependsOn)) {
189
+ const startedContainers = []
190
+ subTask.title = `Container ${
191
+ container._
192
+ } is waiting for ${dependsOn.join(', ')} to start...`
193
+ await Promise.all(
194
+ dependsOn.map(
195
+ async (name) =>
196
+ new Promise((resolve, reject) => {
197
+ const timeout = setTimeout(
198
+ () => {
199
+ reject(
200
+ new Error(
201
+ `Container ${name} not started in time`
202
+ )
203
+ )
204
+ },
205
+ // 2 minutes
206
+ 1000 * 60 * 2
207
+ )
208
+ containerStatuses[name].onStarted.push(
209
+ () => {
210
+ startedContainers.push(name)
211
+ subTask.title = `Container ${
212
+ container._
213
+ } is waiting for ${dependsOn
214
+ .filter(
215
+ (d) =>
216
+ !startedContainers.includes(
217
+ d
218
+ )
219
+ )
220
+ .join(', ')} to start...`
221
+ clearTimeout(timeout)
222
+ resolve()
223
+ }
224
+ )
225
+ })
226
+ )
227
+ )
228
+ }
168
229
 
169
- return
170
- }
230
+ subTask.title = `${container._} is starting...`
171
231
 
172
- // TODO: we might stop containers here ?
173
- await Promise.all(
174
- missingContainers.map((container) =>
175
- containerApi.run(container).then((out) => {
176
- task.output = `From ${container._}: ${out}`
177
- })
178
- )
232
+ await containerApi.run(container)
233
+
234
+ containerStatuses[
235
+ container.nameWithoutPrefix
236
+ ].started = true
237
+ containerStatuses[
238
+ container.nameWithoutPrefix
239
+ ].onStarted.forEach((cb) => {
240
+ cb()
241
+ })
242
+
243
+ subTask.output = `${container._} container started`
244
+ }
245
+ })),
246
+ {
247
+ concurrent: true,
248
+ exitOnError: true
249
+ }
179
250
  )
180
251
  },
181
252
  options: {
@@ -11,10 +11,7 @@ const {
11
11
  getCachedPorts
12
12
  } = require('../../config/get-port-config')
13
13
  const { saveConfiguration } = require('../../config/save-config')
14
- const {
15
- buildProjectImage,
16
- buildDebugProjectImage
17
- } = require('./project-image-builder')
14
+ const { buildProjectImage } = require('./project-image-builder')
18
15
  const checkPHPVersion = require('../requirements/php-version')
19
16
  const { getComposerVersionTask } = require('../composer')
20
17
  const { prepareFileSystem } = require('../file-system')
@@ -190,15 +187,18 @@ Please wait, this will take some time and do not restart the MySQL container unt
190
187
  task.output = 'Dumping MySQL database to dump file...'
191
188
 
192
189
  await containerApi.exec(
193
- [
194
- 'mysqldump',
195
- '--user=root',
196
- '--password=scandipwa',
197
- 'magento',
198
- `--result-file=${path.parse(pathToMySQLDumpFile).base}`
199
- ].join(' '),
200
- containerName,
201
- {},
190
+ {
191
+ container: containerName,
192
+ command: [
193
+ 'mysqldump',
194
+ '--user=root',
195
+ '--password=scandipwa',
196
+ 'magento',
197
+ `--result-file=${
198
+ path.parse(pathToMySQLDumpFile).base
199
+ }`
200
+ ].join(' ')
201
+ },
202
202
  {
203
203
  callback: (t) => {
204
204
  task.output = t
@@ -234,15 +234,7 @@ Please wait, this will take some time and do not restart the MySQL container unt
234
234
  pullImages(),
235
235
  dockerNetwork.tasks.createNetwork(),
236
236
  createVolumes(),
237
- {
238
- task: (ctx, task) =>
239
- task.newListr(
240
- [buildProjectImage(), buildDebugProjectImage()],
241
- {
242
- concurrent: true
243
- }
244
- )
245
- },
237
+ buildProjectImage(),
246
238
  {
247
239
  task: (ctx, subTask) =>
248
240
  subTask.newListr(