@scandipwa/magento-scripts 2.4.10 → 2.4.11

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.
@@ -8,6 +8,13 @@ module.exports = (yargs) => {
8
8
  'exec <container name> [command...]',
9
9
  'Execute command in docker container',
10
10
  (yargs) => {
11
+ yargs.option('non-interactive', {
12
+ alias: 'n',
13
+ type: 'boolean',
14
+ default: false,
15
+ description:
16
+ 'Run in non-interactive mode (for AI terminals and scripts)'
17
+ })
11
18
  yargs.usage(`Usage: npm run exec <container name> [command...]
12
19
 
13
20
  Available containers:
@@ -19,13 +26,20 @@ Available containers:
19
26
  - mariadb
20
27
  - elasticsearch
21
28
  - maildev
22
- - varnish (if enabled)`)
29
+ - varnish (if enabled)
30
+
31
+ Options:
32
+ --non-interactive, -n Run in non-interactive mode (no TTY required)`)
23
33
  },
24
- async () => {
25
- const [containerName, ...commands] = process.argv.slice(3)
34
+ async (argv) => {
35
+ const [containerName, ...commands] = process.argv
36
+ .slice(3)
37
+ .filter((arg) => arg !== '--non-interactive' && arg !== '-n')
38
+
26
39
  await executeTask({
27
40
  containerName,
28
- commands
41
+ commands,
42
+ nonInteractive: !!(argv.nonInteractive || argv.n)
29
43
  })
30
44
  }
31
45
  )
@@ -2,7 +2,7 @@
2
2
  * @returns {import('../../../../../typings/index').ComposerConfiguration}
3
3
  */
4
4
  const composer29 = () => ({
5
- version: '2.9.5'
5
+ version: '2.9.8'
6
6
  })
7
7
 
8
8
  module.exports = composer29
@@ -5,5 +5,6 @@ module.exports = {
5
5
  mariadb106: require('./mariadb-10.6'),
6
6
  mariadb1011: require('./mariadb-10.11'),
7
7
  mariadb114: require('./mariadb-11.4'),
8
- mariadb116: require('./mariadb-11.6')
8
+ mariadb116: require('./mariadb-11.6'),
9
+ mariadb118: require('./mariadb-11.8')
9
10
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @returns {import('../../../../../typings/index').MariaDBConfiguration}
3
+ */
4
+ const mariadb118 = () => ({
5
+ image: 'mariadb:11.8',
6
+ useOptimizerSwitch: true,
7
+ binFileName: 'mariadb',
8
+ binAdminFileName: 'mariadb-admin'
9
+ })
10
+
11
+ module.exports = mariadb118
@@ -1,5 +1,6 @@
1
1
  const valkey80 = require('./valkey-8.0')
2
2
  const valkey81 = require('./valkey-8.1')
3
+ const valkey90 = require('./valkey-9.0')
3
4
 
4
5
  module.exports = {
5
6
  redis50: require('./redis-5.0'),
@@ -8,5 +9,6 @@ module.exports = {
8
9
  redis70: require('./redis-7.0'),
9
10
  redis72: require('./redis-7.2'),
10
11
  valkey80,
11
- valkey81
12
+ valkey81,
13
+ valkey90
12
14
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @returns {import('../../../../typings/index').ServiceWithImage}
3
+ */
4
+ const valkey90 = () => ({
5
+ image: 'valkey/valkey:9.0-alpine'
6
+ })
7
+
8
+ module.exports = valkey90
@@ -7,5 +7,6 @@ module.exports = {
7
7
  varnish74: require('./varnish-7-4'),
8
8
  varnish75: require('./varnish-7-5'),
9
9
  varnish76: require('./varnish-7-6'),
10
- varnish77: require('./varnish-7-7')
10
+ varnish77: require('./varnish-7-7'),
11
+ varnish80: require('./varnish-8-0')
11
12
  }
@@ -0,0 +1,15 @@
1
+ const path = require('path')
2
+
3
+ /**
4
+ * @param {Object} param0
5
+ * @param {string} param0.templateDir
6
+ * @returns {import('../../../../typings/index').VarnishConfiguration}
7
+ */
8
+ const varnish80 = ({ templateDir }) => ({
9
+ enabled: false,
10
+ healthCheck: false,
11
+ image: 'varnish:8.0',
12
+ configTemplate: path.join(templateDir || '', 'varnish.template.vcl')
13
+ })
14
+
15
+ module.exports = varnish80
@@ -0,0 +1,43 @@
1
+ const sodium = require('../services/php/extensions/sodium')
2
+ const {
3
+ magento24PHPExtensionList
4
+ } = require('../magento/required-php-extensions')
5
+ const { php81 } = require('../services/php/versions')
6
+ const { sslTerminator } = require('../services/ssl-terminator')
7
+ const { varnish77 } = require('../services/varnish')
8
+ const { repo } = require('../services/php/base-repo')
9
+ const { nginx128 } = require('../services/nginx/versions')
10
+ const { composer22 } = require('../services/composer/versions')
11
+ const { maildev } = require('../services/maildev')
12
+ const { redis72 } = require('../services/redis')
13
+ const { mariadb106 } = require('../services/mariadb/versions')
14
+ const { elasticsearch717 } = require('../services/elasticsearch/versions')
15
+ const { mysql80 } = require('../services/mysql/versions')
16
+ const { opensearch219 } = require('../services/opensearch/versions')
17
+
18
+ /**
19
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
20
+ */
21
+ module.exports = ({ templateDir }) => ({
22
+ magentoVersion: '2.4.4-p18',
23
+ configuration: {
24
+ php: php81({
25
+ templateDir,
26
+ extensions: { ...magento24PHPExtensionList, sodium },
27
+ baseImage: `${repo}:php-8.1-magento-2.4`
28
+ }),
29
+ nginx: nginx128({ templateDir }),
30
+ redis: redis72(),
31
+ // not supported anymore
32
+ mysql: mysql80(),
33
+ mariadb: mariadb106(),
34
+ // not supported anymore
35
+ elasticsearch: elasticsearch717(),
36
+ composer: composer22(),
37
+ varnish: varnish77({ templateDir }),
38
+ sslTerminator: sslTerminator({ templateDir }),
39
+ maildev: maildev(),
40
+ opensearch: opensearch219(),
41
+ searchengine: 'opensearch'
42
+ }
43
+ })
@@ -0,0 +1,43 @@
1
+ const sodium = require('../services/php/extensions/sodium')
2
+ const {
3
+ magento24PHPExtensionList
4
+ } = require('../magento/required-php-extensions')
5
+ const { php81 } = require('../services/php/versions')
6
+ const { sslTerminator } = require('../services/ssl-terminator')
7
+ const { varnish80 } = require('../services/varnish')
8
+ const { repo } = require('../services/php/base-repo')
9
+ const { nginx128 } = require('../services/nginx/versions')
10
+ const { composer22 } = require('../services/composer/versions')
11
+ const { maildev } = require('../services/maildev')
12
+ const { valkey81 } = require('../services/redis')
13
+ const { mariadb106 } = require('../services/mariadb/versions')
14
+ const { elasticsearch717 } = require('../services/elasticsearch/versions')
15
+ const { mysql80 } = require('../services/mysql/versions')
16
+ const { opensearch219 } = require('../services/opensearch/versions')
17
+
18
+ /**
19
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
20
+ */
21
+ module.exports = ({ templateDir }) => ({
22
+ magentoVersion: '2.4.5-p17',
23
+ configuration: {
24
+ php: php81({
25
+ templateDir,
26
+ extensions: { ...magento24PHPExtensionList, sodium },
27
+ baseImage: `${repo}:php-8.1-magento-2.4`
28
+ }),
29
+ nginx: nginx128({ templateDir }),
30
+ redis: valkey81(),
31
+ // not supported anymore
32
+ mysql: mysql80(),
33
+ mariadb: mariadb106(),
34
+ // not supported anymore
35
+ elasticsearch: elasticsearch717(),
36
+ composer: composer22(),
37
+ varnish: varnish80({ templateDir }),
38
+ sslTerminator: sslTerminator({ templateDir }),
39
+ maildev: maildev(),
40
+ opensearch: opensearch219(),
41
+ searchengine: 'opensearch'
42
+ }
43
+ })
@@ -0,0 +1,43 @@
1
+ const sodium = require('../services/php/extensions/sodium')
2
+ const {
3
+ magento24PHPExtensionList
4
+ } = require('../magento/required-php-extensions')
5
+ const { php81 } = require('../services/php/versions')
6
+ const { sslTerminator } = require('../services/ssl-terminator')
7
+ const { varnish80 } = require('../services/varnish')
8
+ const { repo } = require('../services/php/base-repo')
9
+ const { nginx128 } = require('../services/nginx/versions')
10
+ const { composer22 } = require('../services/composer/versions')
11
+ const { maildev } = require('../services/maildev')
12
+ const { valkey81 } = require('../services/redis')
13
+ const { mariadb1011 } = require('../services/mariadb/versions')
14
+ const { elasticsearch717 } = require('../services/elasticsearch/versions')
15
+ const { mysql80 } = require('../services/mysql/versions')
16
+ const { opensearch219 } = require('../services/opensearch/versions')
17
+
18
+ /**
19
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
20
+ */
21
+ module.exports = ({ templateDir }) => ({
22
+ magentoVersion: '2.4.6-p15',
23
+ configuration: {
24
+ php: php81({
25
+ templateDir,
26
+ extensions: { ...magento24PHPExtensionList, sodium },
27
+ baseImage: `${repo}:php-8.1-magento-2.4`
28
+ }),
29
+ nginx: nginx128({ templateDir }),
30
+ redis: valkey81(),
31
+ // not supported anymore
32
+ mysql: mysql80(),
33
+ mariadb: mariadb1011(),
34
+ // not supported anymore
35
+ elasticsearch: elasticsearch717(),
36
+ composer: composer22(),
37
+ varnish: varnish80({ templateDir }),
38
+ sslTerminator: sslTerminator({ templateDir }),
39
+ maildev: maildev(),
40
+ opensearch: opensearch219(),
41
+ searchengine: 'opensearch'
42
+ }
43
+ })
@@ -0,0 +1,42 @@
1
+ const sodium = require('../services/php/extensions/sodium')
2
+ const {
3
+ magento24PHPExtensionList
4
+ } = require('../magento/required-php-extensions')
5
+ const { php82 } = require('../services/php/versions')
6
+ const { sslTerminator } = require('../services/ssl-terminator')
7
+ const { varnish80 } = require('../services/varnish')
8
+ const { repo } = require('../services/php/base-repo')
9
+ const { nginx128 } = require('../services/nginx/versions')
10
+ const { composer29 } = require('../services/composer/versions')
11
+ const { maildev } = require('../services/maildev')
12
+ const { valkey81 } = require('../services/redis')
13
+ const { mariadb1011 } = require('../services/mariadb/versions')
14
+ const { elasticsearch817 } = require('../services/elasticsearch/versions')
15
+ const { mysql80 } = require('../services/mysql/versions')
16
+ const { opensearch219 } = require('../services/opensearch/versions')
17
+
18
+ /**
19
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
20
+ */
21
+ module.exports = ({ templateDir }) => ({
22
+ magentoVersion: '2.4.7-p10',
23
+ configuration: {
24
+ php: php82({
25
+ templateDir,
26
+ extensions: { ...magento24PHPExtensionList, sodium },
27
+ baseImage: `${repo}:php-8.2-magento-2.4`
28
+ }),
29
+ nginx: nginx128({ templateDir }),
30
+ redis: valkey81(),
31
+ // not supported anymore
32
+ mysql: mysql80(),
33
+ mariadb: mariadb1011(),
34
+ elasticsearch: elasticsearch817(),
35
+ composer: composer29(),
36
+ varnish: varnish80({ templateDir }),
37
+ sslTerminator: sslTerminator({ templateDir }),
38
+ maildev: maildev(),
39
+ opensearch: opensearch219(),
40
+ searchengine: 'opensearch'
41
+ }
42
+ })
@@ -21,7 +21,6 @@ const { opensearch300 } = require('../services/opensearch/versions')
21
21
  */
22
22
  module.exports = ({ templateDir }) => ({
23
23
  magentoVersion: '2.4.8-p4',
24
- isDefault: true,
25
24
  configuration: {
26
25
  php: php83({
27
26
  templateDir,
@@ -0,0 +1,42 @@
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 { varnish80 } = require('../services/varnish')
9
+ const { repo } = require('../services/php/base-repo')
10
+ const { nginx128 } = require('../services/nginx/versions')
11
+ const { composer29 } = 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 { opensearch300 } = require('../services/opensearch/versions')
18
+
19
+ /**
20
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
21
+ */
22
+ module.exports = ({ templateDir }) => ({
23
+ magentoVersion: '2.4.8-p5',
24
+ configuration: {
25
+ php: php83({
26
+ templateDir,
27
+ extensions: { ...magento24PHPExtensionList, sodium, ftp },
28
+ baseImage: `${repo}:php-8.3-magento-2.4`
29
+ }),
30
+ nginx: nginx128({ templateDir }),
31
+ redis: valkey80(),
32
+ mysql: mysql80(),
33
+ mariadb: mariadb114(),
34
+ elasticsearch: elasticsearch817(),
35
+ composer: composer29(),
36
+ varnish: varnish80({ templateDir }),
37
+ sslTerminator: sslTerminator({ templateDir }),
38
+ maildev: maildev(),
39
+ opensearch: opensearch300(),
40
+ searchengine: 'opensearch'
41
+ }
42
+ })
@@ -0,0 +1,44 @@
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 { varnish80 } = require('../services/varnish')
9
+ const { repo } = require('../services/php/base-repo')
10
+ const { nginx128 } = require('../services/nginx/versions')
11
+ const { composer29 } = require('../services/composer/versions')
12
+ const { maildev } = require('../services/maildev')
13
+ const { valkey90 } = require('../services/redis')
14
+ const { mariadb118 } = require('../services/mariadb/versions')
15
+ const { elasticsearch817 } = require('../services/elasticsearch/versions')
16
+ const { mysql84 } = require('../services/mysql/versions')
17
+ const { opensearch300 } = require('../services/opensearch/versions')
18
+
19
+ /**
20
+ * @type {import('../../../typings/common').MagentoVersionConfigurationFunction}
21
+ */
22
+ module.exports = ({ templateDir }) => ({
23
+ magentoVersion: '2.4.9',
24
+ isDefault: true,
25
+ configuration: {
26
+ php: php83({
27
+ templateDir,
28
+ extensions: { ...magento24PHPExtensionList, sodium, ftp },
29
+ baseImage: `${repo}:php-8.5-magento-2.4`
30
+ }),
31
+ nginx: nginx128({ templateDir }),
32
+ redis: valkey90(),
33
+ mysql: mysql84(),
34
+ mariadb: mariadb118(),
35
+ // not supported
36
+ elasticsearch: elasticsearch817(),
37
+ composer: composer29(),
38
+ varnish: varnish80({ templateDir }),
39
+ sslTerminator: sslTerminator({ templateDir }),
40
+ maildev: maildev(),
41
+ opensearch: opensearch300(),
42
+ searchengine: 'opensearch'
43
+ }
44
+ })
@@ -70,10 +70,10 @@ export interface ContainerExecOptions {
70
70
  interactive?: boolean
71
71
  }
72
72
 
73
- export function exec<T>(
73
+ export function exec<T extends boolean = false>(
74
74
  options: ContainerExecOptions,
75
75
  execOptions?: ExecAsyncSpawnOptions<T>
76
- ): Promise<string>
76
+ ): Promise<T extends true ? { code: number; result: string } : string>
77
77
 
78
78
  export function execCommand(options: ContainerExecOptions): string[]
79
79
 
@@ -168,10 +168,10 @@ export interface ContainerRunOptions {
168
168
  platform?: string
169
169
  }
170
170
 
171
- export function run<T>(
171
+ export function run<T extends boolean = false>(
172
172
  containerOptions: ContainerRunOptions,
173
173
  execOptions?: ExecAsyncSpawnOptions<T>
174
- ): Promise<string>
174
+ ): Promise<T extends true ? { code: number; result: string } : string>
175
175
 
176
176
  export function runCommand(options: ContainerRunOptions): string[]
177
177
 
@@ -223,11 +223,13 @@ const buildDockerFileInstructions = async (
223
223
  const { agentVersion, licenseKey } = newRelic
224
224
 
225
225
  // eslint-disable-next-line max-len
226
+ // Use linux-musl archive for Alpine-based images.
227
+ const newRelicArchive = `newrelic-php5-${agentVersion}-linux-musl`
226
228
  dockerFileInstructions.run('apk add --no-cache gcompat')
227
- .run(`curl -L https://download.newrelic.com/php_agent/archive/${agentVersion}/newrelic-php5-${agentVersion}-linux.tar.gz | tar -C /tmp -zx \
229
+ .run(`curl -L https://download.newrelic.com/php_agent/archive/${agentVersion}/${newRelicArchive}.tar.gz | tar -C /tmp -zx \
228
230
  && export NR_INSTALL_USE_CP_NOT_LN=1 \
229
231
  && export NR_INSTALL_SILENT=1 \
230
- && /tmp/newrelic-php5-${agentVersion}-linux/newrelic-install install \
232
+ && /tmp/${newRelicArchive}/newrelic-install install \
231
233
  && rm -rf /tmp/newrelic-php5-* /tmp/nrinstall*`)
232
234
  .run(`sed -i -e "s/REPLACE_WITH_REAL_KEY/${licenseKey}/" \
233
235
  -e "s/newrelic.appname[[:space:]]=[[:space:]].*/newrelic.appname=\\"${
@@ -8,7 +8,9 @@ const { getCachedPorts } = require('../config/get-port-config')
8
8
  const checkPHPVersion = require('./requirements/php-version')
9
9
  const {
10
10
  executeInContainer,
11
- runInContainer
11
+ executeInContainerNonInteractive,
12
+ runInContainer,
13
+ runInContainerNonInteractive
12
14
  } = require('../util/execute-in-container')
13
15
  const { containerApi } = require('./docker/containers')
14
16
  const dockerNetwork = require('./docker/network')
@@ -17,10 +19,11 @@ const { prepareFileSystem } = require('./file-system')
17
19
 
18
20
  /**
19
21
  *
20
- * @param {{ containerName: string, commands: string[] }} argv
22
+ * @param {{ containerName: string, commands: string[], nonInteractive?: boolean }} argv
21
23
  * @returns
22
24
  */
23
25
  const executeTask = async (argv) => {
26
+ const { nonInteractive = false } = argv
24
27
  const tasks = new Listr(
25
28
  [
26
29
  checkRequirements(),
@@ -35,8 +38,12 @@ const executeTask = async (argv) => {
35
38
  {
36
39
  concurrent: false,
37
40
  exitOnError: true,
38
- ctx: { throwMagentoVersionMissing: true },
39
- renderer: process.stdout.isTTY ? 'default' : 'silent',
41
+ ctx: /** @type {any} */ ({ throwMagentoVersionMissing: true }),
42
+ renderer: nonInteractive
43
+ ? 'silent'
44
+ : process.stdout.isTTY
45
+ ? 'default'
46
+ : 'silent',
40
47
  rendererOptions: { collapse: false, clearOutput: true }
41
48
  }
42
49
  )
@@ -45,10 +52,12 @@ const executeTask = async (argv) => {
45
52
  try {
46
53
  ctx = await tasks.run()
47
54
  } catch (e) {
48
- logger.error(e.message || e)
55
+ logger.error(e instanceof Error ? e.message : String(e))
49
56
  process.exit(1)
50
57
  }
51
- const containers = ctx.config.docker.getContainers(ctx.ports)
58
+ const containers = /** @type {Record<string, any>} */ (
59
+ ctx.config.docker.getContainers(ctx.ports)
60
+ )
52
61
  const services = Object.keys(containers)
53
62
 
54
63
  if (
@@ -71,6 +80,11 @@ const executeTask = async (argv) => {
71
80
  ? containerResult[1]
72
81
  : containerResult
73
82
 
83
+ if (nonInteractive && argv.commands.length === 0) {
84
+ logger.error('Non-interactive mode requires a command to execute')
85
+ process.exit(1)
86
+ }
87
+
74
88
  if (argv.commands.length === 0) {
75
89
  // if we have default connect command then use it
76
90
  if (container.connectCommand) {
@@ -88,6 +102,20 @@ const executeTask = async (argv) => {
88
102
  })
89
103
 
90
104
  if (containerList.length > 0) {
105
+ if (nonInteractive) {
106
+ const result = await executeInContainerNonInteractive({
107
+ containerName: container.name,
108
+ commands: argv.commands,
109
+ user: container.user,
110
+ env: container.execCommandEnv
111
+ })
112
+
113
+ if (result.result) {
114
+ process.stdout.write(`${result.result}\n`)
115
+ }
116
+ process.exit(result.code)
117
+ }
118
+
91
119
  if (process.stdout.isTTY) {
92
120
  logger.logN(
93
121
  `Executing container ${logger.style.misc(
@@ -109,6 +137,21 @@ const executeTask = async (argv) => {
109
137
  }
110
138
 
111
139
  if (container.name.includes('php')) {
140
+ if (nonInteractive) {
141
+ const result = await runInContainerNonInteractive(
142
+ {
143
+ ...container,
144
+ name: `${container.name}_exec-${Date.now()}`
145
+ },
146
+ argv.commands
147
+ )
148
+
149
+ if (result.result) {
150
+ process.stdout.write(`${result.result}\n`)
151
+ }
152
+ process.exit(result.code)
153
+ }
154
+
112
155
  if (process.stdout.isTTY) {
113
156
  logger.logN(
114
157
  `Starting container ${logger.style.misc(
@@ -26,7 +26,21 @@ const setupMagento = (options = {}) => ({
26
26
  if (options.onlyInstallMagento) {
27
27
  return task.newListr([
28
28
  flushRedisConfig(),
29
- migrateDatabase({ onlyInstallMagento: true })
29
+ migrateDatabase({ onlyInstallMagento: true }),
30
+ {
31
+ title: 'Disabling Magento caches',
32
+ task: (ctx, task) => {
33
+ const { varnish } =
34
+ ctx.config.overridenConfiguration.configuration
35
+ return task.newListr(
36
+ magentoTask(
37
+ `cache:disable block_html layout${
38
+ !varnish.enabled ? ' full_page' : ''
39
+ }`
40
+ )
41
+ )
42
+ }
43
+ }
30
44
  ])
31
45
  }
32
46
 
@@ -35,6 +49,20 @@ const setupMagento = (options = {}) => ({
35
49
  setupMagentoFilePermissions(),
36
50
  updateEnvPHP(),
37
51
  migrateDatabase(),
52
+ {
53
+ title: 'Disabling Magento caches',
54
+ task: (ctx, task) => {
55
+ const { varnish } =
56
+ ctx.config.overridenConfiguration.configuration
57
+ return task.newListr(
58
+ magentoTask(
59
+ `cache:disable block_html layout${
60
+ !varnish.enabled ? ' full_page' : ''
61
+ }`
62
+ )
63
+ )
64
+ }
65
+ },
38
66
  flushRedisConfig(),
39
67
  {
40
68
  title: 'Configuring Magento settings',
@@ -1,5 +1,30 @@
1
1
  const { spawn } = require('child_process')
2
- const { execCommand, run } = require('../tasks/docker/containers/container-api')
2
+ const {
3
+ execCommand,
4
+ run,
5
+ exec
6
+ } = require('../tasks/docker/containers/container-api')
7
+
8
+ /**
9
+ * Escape an argument for use in a shell command string.
10
+ * Wrap in single quotes and escape single quotes inside.
11
+ * @param {string} arg
12
+ * @returns {string}
13
+ */
14
+ const shellEscapeArg = (arg) => "'" + String(arg).replace(/'/g, "'\\''") + "'"
15
+
16
+ /**
17
+ * Join command args to survive shell re-parsing in `docker run`.
18
+ * @param {string[]} args
19
+ * @returns {string}
20
+ */
21
+ const joinCommandArgs = (...args) =>
22
+ args
23
+ .map((arg) => {
24
+ const value = String(arg)
25
+ return /[\s'"\\$`]/.test(value) ? shellEscapeArg(value) : value
26
+ })
27
+ .join(' ')
3
28
 
4
29
  /**
5
30
  * @param {{ containerName: string, commands: string[], user?: string, env?: Record<string, string> }} param0
@@ -30,6 +55,37 @@ const executeInContainer = ({ containerName, commands, user, env }) => {
30
55
  })
31
56
  }
32
57
 
58
+ /**
59
+ * Non-interactive version of executeInContainer for AI terminals and scripts.
60
+ * @param {{ containerName: string, commands: string[], user?: string, workdir?: string, env?: Record<string, string> }} param0
61
+ * @returns {Promise<{ code: number, result: string }>}
62
+ */
63
+ const executeInContainerNonInteractive = async ({
64
+ containerName,
65
+ commands,
66
+ user,
67
+ workdir,
68
+ env
69
+ }) => {
70
+ const [commandBin, ...commandsArgs] = commands
71
+ const fullCommand = joinCommandArgs(commandBin, ...commandsArgs)
72
+
73
+ return exec(
74
+ {
75
+ container: containerName,
76
+ command: fullCommand,
77
+ user,
78
+ workdir,
79
+ tty: false,
80
+ interactive: false,
81
+ env: env || {}
82
+ },
83
+ {
84
+ withCode: true
85
+ }
86
+ )
87
+ }
88
+
33
89
  /**
34
90
  * @param {import('../tasks/docker/containers/container-api').ContainerRunOptions} options
35
91
  * @param {string[]} commands
@@ -45,7 +101,7 @@ const runInContainer = async (options, commands) => {
45
101
  const runResult = await run(
46
102
  {
47
103
  ...options,
48
- command: `${commandBin} ${commandsArgs.join(' ')}`,
104
+ command: joinCommandArgs(commandBin, ...commandsArgs),
49
105
  tty: true,
50
106
  detach: false,
51
107
  rm: true
@@ -59,7 +115,32 @@ const runInContainer = async (options, commands) => {
59
115
  process.exit(runResult.code)
60
116
  }
61
117
 
118
+ /**
119
+ * Non-interactive version of runInContainer for AI terminals and scripts.
120
+ * @param {import('../tasks/docker/containers/container-api').ContainerRunOptions} options
121
+ * @param {string[]} commands
122
+ * @returns {Promise<{ code: number, result: string }>}
123
+ */
124
+ const runInContainerNonInteractive = async (options, commands) => {
125
+ const [commandBin, ...commandsArgs] = commands
126
+
127
+ return run(
128
+ {
129
+ ...options,
130
+ command: joinCommandArgs(commandBin, ...commandsArgs),
131
+ tty: false,
132
+ detach: false,
133
+ rm: true
134
+ },
135
+ {
136
+ withCode: true
137
+ }
138
+ )
139
+ }
140
+
62
141
  module.exports = {
63
142
  executeInContainer,
64
- runInContainer
143
+ executeInContainerNonInteractive,
144
+ runInContainer,
145
+ runInContainerNonInteractive
65
146
  }
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.10",
6
+ "version": "2.4.11",
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": "c901bd4d25f0e127f9fc286ba979e49d2a145757"
62
+ "gitHead": "64c177af194cf32de4d8ae08116f016d30ec7f01"
63
63
  }