@scandipwa/magento-scripts 2.0.0-alpha.2 → 2.0.0-alpha.21

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 (146) hide show
  1. package/exec.js +71 -0
  2. package/index.js +1 -1
  3. package/lib/commands/cli.js +18 -1
  4. package/lib/commands/execute.js +2 -57
  5. package/lib/commands/logs.js +28 -2
  6. package/lib/commands/start.js +24 -3
  7. package/lib/commands/status.js +9 -1
  8. package/lib/config/config.js +20 -2
  9. package/lib/config/docker.js +90 -53
  10. package/lib/config/get-project-configuration.js +5 -0
  11. package/lib/config/index.js +10 -3
  12. package/lib/config/php/versions/php-7.2.js +1 -0
  13. package/lib/config/php/versions/php-7.3.js +1 -0
  14. package/lib/config/php/versions/php-7.4.js +1 -0
  15. package/lib/config/php/versions/php-8.1.js +1 -0
  16. package/lib/config/php-config.js +4 -3
  17. package/lib/config/port-config.js +3 -1
  18. package/lib/config/services/composer/versions/composer-1.js +13 -0
  19. package/lib/config/services/composer/versions/composer-2.js +8 -0
  20. package/lib/config/services/composer/versions/index.js +4 -0
  21. package/lib/config/services/maildev/index.js +7 -0
  22. package/lib/config/services/nginx/versions/index.js +3 -0
  23. package/lib/config/services/nginx/versions/nginx-1.18.js +11 -0
  24. package/lib/config/{ssl-terminator → services/ssl-terminator}/index.js +3 -0
  25. package/lib/config/templates/magentorc.template +12 -5
  26. package/lib/config/templates/php-debug.template.ini +31 -0
  27. package/lib/config/templates/php-fpm.template.conf +1 -2
  28. package/lib/config/templates/php.template.ini +5 -201
  29. package/lib/config/templates/ssl-terminator.template.conf +10 -3
  30. package/lib/config/templates/varnish.template.vcl +20 -13
  31. package/lib/config/varnish/varnish-6-0.js +4 -0
  32. package/lib/config/varnish/varnish-6-6.js +4 -0
  33. package/lib/config/varnish/varnish-7-0.js +4 -0
  34. package/lib/config/versions/magento-2.2.10.js +41 -0
  35. package/lib/config/versions/magento-2.3.0.js +8 -10
  36. package/lib/config/versions/magento-2.3.1.js +8 -10
  37. package/lib/config/versions/magento-2.3.2-p1.js +8 -10
  38. package/lib/config/versions/magento-2.3.2-p2.js +8 -10
  39. package/lib/config/versions/magento-2.3.2.js +8 -10
  40. package/lib/config/versions/magento-2.3.3-p1.js +8 -10
  41. package/lib/config/versions/magento-2.3.3.js +8 -10
  42. package/lib/config/versions/magento-2.3.4-p1.js +8 -10
  43. package/lib/config/versions/magento-2.3.4-p2.js +8 -10
  44. package/lib/config/versions/magento-2.3.4.js +8 -10
  45. package/lib/config/versions/magento-2.3.5-p1.js +8 -10
  46. package/lib/config/versions/magento-2.3.5-p2.js +8 -10
  47. package/lib/config/versions/magento-2.3.5.js +8 -10
  48. package/lib/config/versions/magento-2.3.6-p1.js +8 -10
  49. package/lib/config/versions/magento-2.3.6.js +8 -10
  50. package/lib/config/versions/magento-2.3.7-p1.js +8 -10
  51. package/lib/config/versions/magento-2.3.7-p2.js +8 -10
  52. package/lib/config/versions/magento-2.3.7-p3.js +8 -10
  53. package/lib/config/versions/magento-2.3.7-p4.js +42 -0
  54. package/lib/config/versions/magento-2.3.7.js +8 -10
  55. package/lib/config/versions/magento-2.4.0-p1.js +8 -10
  56. package/lib/config/versions/magento-2.4.0.js +8 -10
  57. package/lib/config/versions/magento-2.4.1-p1.js +8 -10
  58. package/lib/config/versions/magento-2.4.1.js +8 -10
  59. package/lib/config/versions/magento-2.4.2-p1.js +8 -10
  60. package/lib/config/versions/magento-2.4.2-p2.js +8 -10
  61. package/lib/config/versions/magento-2.4.2.js +8 -10
  62. package/lib/config/versions/magento-2.4.3-p1.js +8 -10
  63. package/lib/config/versions/magento-2.4.3-p2.js +8 -10
  64. package/lib/config/versions/magento-2.4.3-p3.js +44 -0
  65. package/lib/config/versions/magento-2.4.3.js +8 -10
  66. package/lib/config/versions/magento-2.4.4-p1.js +44 -0
  67. package/lib/config/versions/magento-2.4.4.js +8 -10
  68. package/lib/config/versions/magento-2.4.5.js +44 -0
  69. package/lib/tasks/cli/create-bashrc-config.js +4 -2
  70. package/lib/tasks/composer/local-auth-json.js +1 -1
  71. package/lib/tasks/database/connect-to-database.js +6 -3
  72. package/lib/tasks/database/create-magento-database.js +5 -2
  73. package/lib/tasks/database/create-magento-user.js +50 -0
  74. package/lib/tasks/database/default-magento-database.js +3 -0
  75. package/lib/tasks/database/default-magento-user.js +7 -0
  76. package/lib/tasks/database/import-dump-to-database.js +3 -2
  77. package/lib/tasks/docker/api.d.ts +25 -1
  78. package/lib/tasks/docker/api.js +31 -1
  79. package/lib/tasks/docker/containers/container-api.d.ts +21 -0
  80. package/lib/tasks/docker/containers/container-api.js +82 -17
  81. package/lib/tasks/docker/containers/tasks.js +44 -13
  82. package/lib/tasks/docker/convert-composer-home-to-composer-cache-volume.js +52 -0
  83. package/lib/tasks/docker/convert-mysql-to-mariadb.js +2 -2
  84. package/lib/tasks/docker/image/image-api.d.ts +44 -0
  85. package/lib/tasks/docker/image/image-api.js +30 -2
  86. package/lib/tasks/docker/index.js +6 -1
  87. package/lib/tasks/docker/project-image-builder.js +37 -14
  88. package/lib/tasks/docker/system/index.js +5 -0
  89. package/lib/tasks/docker/system/system-api.d.ts +71 -0
  90. package/lib/tasks/docker/system/system-api.js +29 -0
  91. package/lib/tasks/docker/volume/index.js +2 -1
  92. package/lib/tasks/docker/volume/tasks.js +67 -9
  93. package/lib/tasks/docker/volume/volume-api.d.ts +40 -0
  94. package/lib/tasks/docker/volume/volume-api.js +54 -1
  95. package/lib/tasks/execute.js +100 -0
  96. package/lib/tasks/file-system/create-nginx-config.js +3 -5
  97. package/lib/tasks/file-system/create-php-config.js +2 -23
  98. package/lib/tasks/file-system/create-php-debug-config.js +45 -0
  99. package/lib/tasks/file-system/create-php-fpm-config.js +2 -4
  100. package/lib/tasks/file-system/create-phpstorm-config/exclude-folder-config.js +13 -3
  101. package/lib/tasks/file-system/create-phpstorm-config/index.js +2 -1
  102. package/lib/tasks/file-system/create-phpstorm-config/workspace-config/php-workspace-project-configuration-config.js +1 -1
  103. package/lib/tasks/file-system/create-ssl-terminator-config.js +4 -7
  104. package/lib/tasks/file-system/create-varnish-config.js +4 -7
  105. package/lib/tasks/file-system/create-vscode-config.js +2 -1
  106. package/lib/tasks/file-system/index.js +3 -2
  107. package/lib/tasks/magento/enable-magento-composer-plugins.js +85 -30
  108. package/lib/tasks/magento/install-magento-project.js +3 -2
  109. package/lib/tasks/magento/setup-magento/configure-elasticsearch.js +2 -4
  110. package/lib/tasks/magento/setup-magento/flush-redis-config.js +3 -6
  111. package/lib/tasks/magento/setup-magento/index.js +2 -0
  112. package/lib/tasks/magento/setup-magento/install-magento.js +8 -13
  113. package/lib/tasks/magento/setup-magento/set-base-url.js +1 -1
  114. package/lib/tasks/magento/setup-magento/set-mail-config.js +22 -0
  115. package/lib/tasks/magento/setup-magento/varnish-config.js +4 -9
  116. package/lib/tasks/magento/setup-magento/waiting-for-varnish.js +17 -16
  117. package/lib/tasks/php/php-container.js +7 -3
  118. package/lib/tasks/php/update-env-php.js +3 -4
  119. package/lib/tasks/{prefix → project-config}/index.js +6 -6
  120. package/lib/tasks/requirements/composer-credentials.js +7 -3
  121. package/lib/tasks/requirements/docker/context.js +88 -0
  122. package/lib/tasks/requirements/docker/index.js +114 -20
  123. package/lib/tasks/requirements/docker/install.js +21 -7
  124. package/lib/tasks/requirements/docker/permissions.js +2 -11
  125. package/lib/tasks/requirements/docker/running-status.js +94 -24
  126. package/lib/tasks/requirements/docker/version.js +1 -0
  127. package/lib/tasks/requirements/index.js +0 -2
  128. package/lib/tasks/requirements/php-version.js +4 -2
  129. package/lib/tasks/start.js +27 -8
  130. package/lib/tasks/status/index.js +60 -21
  131. package/lib/tasks/stop.js +2 -0
  132. package/lib/tasks/theme/retrieve-theme-data.js +12 -2
  133. package/lib/util/config-file-validator.js +17 -3
  134. package/lib/util/execute-in-container.js +62 -0
  135. package/lib/util/instance-metadata.js +14 -2
  136. package/lib/util/systemctl.js +62 -13
  137. package/package.json +4 -3
  138. package/typings/context.d.ts +11 -0
  139. package/typings/index.d.ts +46 -1
  140. package/lib/tasks/execute/index.js +0 -26
  141. package/lib/tasks/requirements/dependency/arch.js +0 -50
  142. package/lib/tasks/requirements/dependency/centos.js +0 -36
  143. package/lib/tasks/requirements/dependency/fedora.js +0 -36
  144. package/lib/tasks/requirements/dependency/index.js +0 -33
  145. package/lib/tasks/requirements/dependency/mac.js +0 -124
  146. package/lib/tasks/requirements/dependency/ubuntu.js +0 -83
@@ -1,7 +1,6 @@
1
1
  import { ExecAsyncSpawnOptions } from '../../util/exec-async-command';
2
2
 
3
3
  export interface DockerVersionOptions<T extends boolean = false> {
4
- format?: string
5
4
  format?: string
6
5
  formatToJSON?: T
7
6
  }
@@ -69,3 +68,28 @@ export function version(
69
68
  options: DockerVersionOptions<true>,
70
69
  execOptions?: ExecAsyncSpawnOptions<false>
71
70
  ): Promise<DockerVersionResult>
71
+
72
+ export interface DockerContextOptions<T extends boolean = false> {
73
+ format?: string
74
+ formatToJSON?: T
75
+ }
76
+
77
+ export interface DockerContextResult {
78
+ Current: boolean
79
+ Description: string
80
+ DockerEndpoint: string
81
+ KubernetesEndpoint: string
82
+ ContextType: string
83
+ Name: string
84
+ StackOrchestrator: string
85
+ }
86
+
87
+ export function context(
88
+ options: DockerContextOptions,
89
+ execOptions?: ExecAsyncSpawnOptions<false>
90
+ ): Promise<string>
91
+
92
+ export function context(
93
+ options: DockerContextOptions<true>,
94
+ execOptions?: ExecAsyncSpawnOptions<false>
95
+ ): Promise<DockerContextResult[]>
@@ -25,6 +25,36 @@ const version = async (options, execOptions = {}) => {
25
25
  return execAsyncSpawn(`docker version ${args}`, execOptions);
26
26
  };
27
27
 
28
+ /**
29
+ * @param {import('./api').DockerContextOptions} options
30
+ * @param {import('../../util/exec-async-command').ExecAsyncSpawnOptions} execOptions
31
+ */
32
+ const context = async (options, execOptions = {}) => {
33
+ const {
34
+ format,
35
+ formatToJSON
36
+ } = options;
37
+
38
+ const formatArg = !formatToJSON && format
39
+ ? `--format=${format}`
40
+ : formatToJSON && '--format=\'{{json .}}\'';
41
+ const args = [
42
+ formatArg
43
+ ].filter(Boolean).join(' ');
44
+
45
+ if (formatToJSON) {
46
+ const result = await execAsyncSpawn(`docker context ls ${args}`, execOptions);
47
+ if (result.startsWith('[')) {
48
+ return JSON.parse(result);
49
+ }
50
+
51
+ return JSON.parse(`[${result.split('\n').join(', ')}]`);
52
+ }
53
+
54
+ return execAsyncSpawn(`docker context ls ${args}`, execOptions);
55
+ };
56
+
28
57
  module.exports = {
29
- version
58
+ version,
59
+ context
30
60
  };
@@ -69,6 +69,8 @@ export interface ContainerRunOptions {
69
69
  * Run container in background and print container ID
70
70
  */
71
71
  detach?: boolean
72
+
73
+ tty?: boolean
72
74
  /**
73
75
  * Publish or expose port [docs](https://docs.docker.com/engine/reference/commandline/run/#publish-or-expose-port--p---expose)
74
76
  */
@@ -126,3 +128,22 @@ export interface ContainerRunOptions {
126
128
  }
127
129
 
128
130
  export function run(containerOptions: ContainerRunOptions, execOptions?: ExecAsyncSpawnOptions<false>): Promise<false>
131
+
132
+ export function runCommand(options: ContainerRunOptions): string[]
133
+
134
+ export interface ContainerLogsOptions<T = never> {
135
+ name: string
136
+ details?: boolean
137
+ follow?: boolean
138
+ since?: string
139
+ tail?: string
140
+ timestamps?: boolean
141
+ until?: string
142
+ parser?: (line: string) => T
143
+ }
144
+
145
+ export function logs(options?: ContainerLogsOptions, execOptions?: ExecAsyncSpawnOptions<false>): Promise<string>
146
+ export function logs<T>(options?: ContainerLogsOptions<T>, execOptions?: ExecAsyncSpawnOptions<false>): Promise<T[]>
147
+
148
+ export function stop(containers: string[], execOptions: ExecAsyncSpawnOptions<false>): Promise<string>
149
+ export function rm(containers: string[], execOptions: ExecAsyncSpawnOptions<false>): Promise<string>
@@ -3,9 +3,9 @@ const { execAsyncSpawn } = require('../../../util/exec-async-command');
3
3
 
4
4
  /**
5
5
  * @param {import('./container-api').ContainerRunOptions} options
6
- * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions<false>} execOptions
6
+ * @returns {string[]}
7
7
  */
8
- const run = (options, execOptions = {}) => {
8
+ const runCommand = (options) => {
9
9
  const {
10
10
  addHost,
11
11
  ports = [],
@@ -24,32 +24,35 @@ const run = (options, execOptions = {}) => {
24
24
  expose = [],
25
25
  detach = true,
26
26
  rm = false,
27
+ tty = false,
27
28
  user
28
29
  } = options;
29
30
 
30
31
  const detachArg = detach && '-d';
31
32
  const rmArg = rm && '--rm';
33
+ const ttyArg = tty && '-it';
32
34
  const exposeArg = expose && expose.map((e) => `--expose=${ e }`);
33
- const restartArg = !rm && restart && `--restart ${ restart }`;
34
- const networkArg = network && `--network ${ network }`;
35
- const portsArgs = ports.map((port) => `-p ${ port }`).join(' ');
36
- const mountsArgs = mounts.map((mount) => `--mount ${ mount }`).join(' ');
37
- const mountVolumesArgs = mountVolumes.map((mount) => `-v ${mount}`).join(' ');
38
- const envArgs = !env ? '' : Object.entries(env).map(([key, value]) => `--env ${ key }='${ value }'`).join(' ');
39
- const nameArg = name && `--name ${name}`;
40
- const entrypointArg = entrypoint && `--entrypoint "${entrypoint}"`;
41
- const healthCheckArg = healthCheck && Object.entries(healthCheck).map(([key, value]) => `--health-${key} '${value}'`).join(' ');
42
- const securityArg = securityOptions.length > 0 && securityOptions.map((opt) => `--security-opt ${opt}`).join(' ');
43
- const tmpfsArg = tmpfs.length > 0 && tmpfs.map((t) => `--tmpfs ${t}`).join(' ');
35
+ const restartArg = !rm && restart && `--restart=${ restart }`;
36
+ const networkArg = network && `--network=${ network }`;
37
+ const portsArgs = ports && ports.length > 0 && ports.map((port) => `-p=${ port }`);
38
+ const mountsArgs = mounts && mounts.map((mount) => `--mount=${ mount }`);
39
+ const mountVolumesArgs = mountVolumes && mountVolumes.map((mount) => `-v=${mount}`);
40
+ const envArgs = !env ? '' : Object.entries(env).map(([key, value]) => `--env=${ key }='${ value }'`);
41
+ const nameArg = name && `--name=${name}`;
42
+ const entrypointArg = entrypoint && `--entrypoint="${entrypoint}"`;
43
+ const healthCheckArg = healthCheck && Object.entries(healthCheck).map(([key, value]) => `--health-${key}='${value}'`);
44
+ const securityArg = securityOptions.length > 0 && securityOptions.map((opt) => `--security-opt=${opt}`);
45
+ const tmpfsArg = tmpfs.length > 0 && tmpfs.map((t) => `--tmpfs=${t}`);
44
46
  const userArg = user && `--user=${user}`;
45
47
  const addHostArg = addHost && `--add-host=${addHost}`;
46
48
 
47
49
  const dockerCommand = [
48
50
  'docker',
49
51
  'run',
52
+ nameArg,
53
+ ttyArg,
50
54
  detachArg,
51
55
  rmArg,
52
- nameArg,
53
56
  networkArg,
54
57
  restartArg,
55
58
  portsArgs,
@@ -65,11 +68,17 @@ const run = (options, execOptions = {}) => {
65
68
  addHostArg,
66
69
  image,
67
70
  command
68
- ].filter(Boolean).join(' ');
71
+ ].flat().filter(Boolean).filter((arg) => typeof arg === 'string');
69
72
 
70
- return execAsyncSpawn(dockerCommand, execOptions);
73
+ return dockerCommand;
71
74
  };
72
75
 
76
+ /**
77
+ * @param {import('./container-api').ContainerRunOptions} options
78
+ * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions<false>} execOptions
79
+ */
80
+ const run = (options, execOptions = {}) => execAsyncSpawn(runCommand(options).join(' '), execOptions);
81
+
73
82
  /**
74
83
  * @param {string} command
75
84
  * @param {string} container container id or name
@@ -140,14 +149,70 @@ const ls = async (options = {}, execOptions = {}) => {
140
149
 
141
150
  if (formatToJSON) {
142
151
  const result = await execAsyncSpawn(`docker container ls ${args}`, execOptions);
152
+ if (result.startsWith('[')) {
153
+ return JSON.parse(result);
154
+ }
155
+
143
156
  return JSON.parse(`[${result.split('\n').join(', ')}]`);
144
157
  }
145
158
 
146
159
  return execAsyncSpawn(`docker container ls ${args}`, execOptions);
147
160
  };
148
161
 
162
+ /**
163
+ * @param {import('./container-api').ContainerLogsOptions} options
164
+ * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions<false>} execOptions
165
+ */
166
+ const logs = async (options = {}, execOptions = {}) => {
167
+ const {
168
+ name,
169
+ details = false,
170
+ follow = false,
171
+ since = '',
172
+ tail = '',
173
+ timestamps = false,
174
+ until = '',
175
+ parser
176
+ } = options;
177
+ const detailsArg = details && '--details';
178
+ const followArg = follow && '--follow';
179
+ const sinceArg = since && `--since=${since}`;
180
+ const tailArg = tail && `--tail=${tail}`;
181
+ const timestampsArg = timestamps && '--timestamps';
182
+ const untilArg = until && `--until=${until}`;
183
+
184
+ const logsCommand = [
185
+ 'docker',
186
+ 'container',
187
+ 'logs',
188
+ detailsArg,
189
+ followArg,
190
+ sinceArg,
191
+ tailArg,
192
+ timestampsArg,
193
+ untilArg,
194
+ name
195
+ ].filter(Boolean).join(' ');
196
+
197
+ if (parser) {
198
+ const result = await execAsyncSpawn(logsCommand, execOptions);
199
+
200
+ return result.split('\n').map((line) => parser(line));
201
+ }
202
+
203
+ return execAsyncSpawn(logsCommand, execOptions);
204
+ };
205
+
206
+ const stop = (containers, execOptions = {}) => execAsyncSpawn(`docker container stop ${containers.join(' ')}`, execOptions);
207
+
208
+ const rm = (containers, execOptions = {}) => execAsyncSpawn(`docker container rm ${containers.join(' ')}`, execOptions);
209
+
149
210
  module.exports = {
150
211
  run,
212
+ runCommand,
151
213
  exec,
152
- ls
214
+ ls,
215
+ logs,
216
+ stop,
217
+ rm
153
218
  };
@@ -6,9 +6,9 @@ const containerApi = require('./container-api');
6
6
  const { imageApi } = require('../image');
7
7
  const { execAsyncSpawn } = require('../../../util/exec-async-command');
8
8
 
9
- const stop = async (containers) => {
10
- await execAsyncSpawn(`docker container stop ${containers.join(' ')}`);
11
- await execAsyncSpawn(`docker container rm ${containers.join(' ')}`);
9
+ const stopAndRemoveContainers = async (containers) => {
10
+ await containerApi.stop(containers);
11
+ await containerApi.rm(containers);
12
12
  };
13
13
 
14
14
  const pull = async (image) => execAsyncSpawn(`docker pull ${image}`);
@@ -26,8 +26,39 @@ const remoteImageReducer = (acc, val) => {
26
26
  */
27
27
  const pullImages = () => ({
28
28
  title: 'Pulling container images',
29
- task: async ({ config: { docker } }, task) => {
29
+ task: async ({ config: { docker }, pullImages }, task) => {
30
30
  const containers = Object.values(docker.getContainers());
31
+
32
+ if (pullImages) {
33
+ return task.newListr(
34
+ containers
35
+ .reduce(remoteImageReducer, [])
36
+ .map((image) => {
37
+ const [repo, tag = 'latest'] = image.split(':');
38
+
39
+ return { repo, tag };
40
+ })
41
+ .reduce(
42
+ (acc, val) => acc.concat(
43
+ acc.some(
44
+ (c) => c.repo === val.repo
45
+ && c.tag === val.tag
46
+ )
47
+ ? []
48
+ : val
49
+ ),
50
+ []
51
+ )
52
+ .map(({ repo, tag }) => ({
53
+ title: `Pulling ${ logger.style.file(`${repo}:${tag}`) } image`,
54
+ task: () => pull(`${repo}:${tag}`)
55
+ })), {
56
+ concurrent: true,
57
+ exitOnError: true
58
+ }
59
+ );
60
+ }
61
+
31
62
  const imagesFilter = containers
32
63
  .reduce(remoteImageReducer, [])
33
64
  .map((image) => `reference='${image}'`);
@@ -85,10 +116,10 @@ const pullImages = () => ({
85
116
  const startContainers = () => ({
86
117
  title: 'Starting containers',
87
118
  task: async ({ ports, config: { docker }, debug }, task) => {
88
- const containerList = (await execAsyncSpawn('docker container ls --all --format="{{.Names}}"')).split('\n');
119
+ const containerList = await containerApi.ls({ formatToJSON: true, all: true });
89
120
 
90
121
  const missingContainers = Object.values(docker.getContainers(ports)).filter(
91
- ({ name }) => !containerList.includes(name)
122
+ ({ name }) => !containerList.some((c) => c.Names === name)
92
123
  );
93
124
 
94
125
  if (missingContainers.length === 0) {
@@ -129,22 +160,22 @@ const startContainers = () => ({
129
160
  const stopContainers = () => ({
130
161
  title: 'Stopping Docker containers',
131
162
  task: async ({ config: { baseConfig: { prefix } } }, task) => {
132
- const containerList = (await execAsyncSpawn('docker container ls --all --format="{{.Names}}"')).split('\n');
163
+ const containerList = await containerApi.ls({ formatToJSON: true, all: true });
133
164
 
134
- const runningContainers = containerList.filter((containerName) => containerName.startsWith(prefix));
165
+ const runningContainers = containerList.filter((containerName) => containerName.Names.startsWith(prefix));
135
166
 
136
167
  if (runningContainers.length === 0) {
137
168
  task.skip();
138
169
  return;
139
170
  }
140
171
 
141
- await stop(runningContainers);
172
+ await stopAndRemoveContainers(runningContainers.map(({ Names }) => Names));
142
173
  }
143
174
  });
144
175
 
145
176
  const getContainerStatus = async (containerName) => {
146
177
  try {
147
- return JSON.parse(await execAsyncSpawn(`docker inspect --format='{{json .State}}' ${containerName}`));
178
+ return JSON.parse(await execAsyncSpawn(`docker inspect --format='{{json .}}' ${containerName}`));
148
179
  } catch {
149
180
  return null;
150
181
  }
@@ -167,11 +198,11 @@ const checkContainersAreRunning = () => ({
167
198
  }))
168
199
  );
169
200
 
170
- if (containersWithStatus.some((c) => c.status.Status !== 'running')) {
201
+ if (containersWithStatus.some((c) => c.status.State.Status !== 'running')) {
171
202
  if (tries === 2) {
172
- throw new KnownError(`${containersWithStatus.filter((c) => c.status.Status !== 'running').map((c) => c._).join(', ')} containers are not running! Please check container logs for more details!`);
203
+ throw new KnownError(`${containersWithStatus.filter((c) => c.status.State.Status !== 'running').map((c) => c._).join(', ')} containers are not running! Please check container logs for more details!`);
173
204
  } else {
174
- task.output = `${containersWithStatus.filter((c) => c.status.Status !== 'running').map((c) => c._).join(', ')} are not running, waiting if something will change...`;
205
+ task.output = `${containersWithStatus.filter((c) => c.status.State.Status !== 'running').map((c) => c._).join(', ')} are not running, waiting if something will change...`;
175
206
  await sleep(2000);
176
207
  tries++;
177
208
  }
@@ -0,0 +1,52 @@
1
+ const { containerApi } = require('./containers');
2
+ const volumeApi = require('./volume/volume-api');
3
+
4
+ const composeHomeDataVolumeName = 'composer_home-data';
5
+
6
+ /**
7
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
8
+ */
9
+ const convertComposerHomeToComposerCacheVolume = () => ({
10
+ skip: async () => {
11
+ const volumeList = await volumeApi.ls({
12
+ formatToJSON: true,
13
+ filter: `name=${ composeHomeDataVolumeName }`
14
+ });
15
+
16
+ return volumeList.length === 0;
17
+ },
18
+ task: async (ctx, task) => {
19
+ if (ctx.platform === 'linux' && !ctx.isDockerDesktop) {
20
+ await volumeApi.rm({ volumes: [composeHomeDataVolumeName] });
21
+ return;
22
+ }
23
+
24
+ const { composer_cache } = ctx.config.docker.volumes;
25
+ task.title = `Migrating from ${ composer_cache.name } volume to ${ composeHomeDataVolumeName }...`;
26
+ await containerApi.run({
27
+ rm: true,
28
+ detach: false,
29
+ mountVolumes: [
30
+ `${ composeHomeDataVolumeName }:/from:ro`,
31
+ `${ composer_cache.name }:/to`
32
+ ],
33
+ image: 'alpine',
34
+ command: 'ash -c "cd /from/cache; cp -av . /to"'
35
+ });
36
+
37
+ const runningContainers = await volumeApi.inspect({ volume: composeHomeDataVolumeName, formatToJSON: true });
38
+
39
+ if (runningContainers.Containers && Object.entries(runningContainers.Containers).length > 0) {
40
+ await Promise.all(Object.values(runningContainers.Containers).map(async (c) => {
41
+ await containerApi.stop([c.Name]);
42
+ await containerApi.rm([c.Name]);
43
+ }));
44
+ }
45
+
46
+ await volumeApi.rm({ volumes: [composeHomeDataVolumeName] });
47
+ }
48
+ });
49
+
50
+ module.exports = {
51
+ convertComposerHomeToComposerCacheVolume
52
+ };
@@ -5,7 +5,6 @@ const { volumeApi, createVolumes } = require('./volume');
5
5
  const { execAsyncSpawn } = require('../../util/exec-async-command');
6
6
  const { containerApi, stopContainers, pullImages } = require('./containers');
7
7
  const { importDumpToDatabase, connectToDatabase } = require('../database');
8
- const { setPrefix } = require('../prefix');
9
8
  const getProjectConfiguration = require('../../config/get-project-configuration');
10
9
  const { getAvailablePorts, getCachedPorts } = require('../../config/get-port-config');
11
10
  const { saveConfiguration } = require('../../config/save-config');
@@ -21,6 +20,7 @@ const KnownError = require('../../errors/known-error');
21
20
  const { createCacheFolder } = require('../cache');
22
21
  const { getSystemConfigTask } = require('../../config/system-config');
23
22
  const sleep = require('../../util/sleep');
23
+ const { setProjectConfigTask } = require('../project-config');
24
24
 
25
25
  /**
26
26
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
@@ -203,7 +203,7 @@ Please wait, this will take some time and do not restart the MySQL container unt
203
203
  getSystemConfigTask(),
204
204
  getCachedPorts(),
205
205
  stopContainers(),
206
- setPrefix(),
206
+ setProjectConfigTask(),
207
207
  getProjectConfiguration(),
208
208
  // get fresh ports
209
209
  getAvailablePorts(),
@@ -32,3 +32,47 @@ export function ls(
32
32
  options?: ImagesLsOptions<true>,
33
33
  execOptions?: ExecAsyncSpawnOptions<false>
34
34
  ): Promise<ImagesLsResult[]>
35
+
36
+ export interface ImagesInspectOptions<T extends boolean = false> {
37
+ image: string
38
+ format?: string
39
+ formatToJSON?: T
40
+ }
41
+
42
+ export interface ImagesInspectResult {
43
+ Id: string
44
+ RepoTags: string[]
45
+ RepoDigests: string[]
46
+ Parent: string
47
+ Comment: string
48
+ Created: string
49
+ Container: string
50
+ ContainerConfig: unknown
51
+ DockerVersion: string
52
+ Author: string
53
+ Config: {
54
+ Env: string[],
55
+ Cmd: string[]
56
+ WorkingDir: string
57
+ Entrypoint: string[]
58
+ StopSignal: string
59
+ }
60
+ Architecture: string
61
+ Os: string
62
+ Size: number
63
+ VirtualSize: number
64
+ GraphDriver: unknown
65
+ RootFs: unknown
66
+ Metadata: Record<string, unknown>
67
+ CreatedTime: number
68
+ Container: Record<string, unknown>
69
+ }
70
+
71
+ export function inspect(
72
+ options?: ImagesInspectOptions,
73
+ execOptions?: ExecAsyncSpawnOptions<false>
74
+ ): Promise<string>
75
+ export function inspect(
76
+ options?: ImagesInspectOptions<true>,
77
+ execOptions?: ExecAsyncSpawnOptions<false>
78
+ ): Promise<ImagesInspectResult>
@@ -1,7 +1,6 @@
1
1
  const { execAsyncSpawn } = require('../../../util/exec-async-command');
2
2
 
3
3
  /**
4
- *
5
4
  * @param {import('./image-api').ImagesLsOptions} options
6
5
  * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions} execOptions
7
6
  */
@@ -44,6 +43,35 @@ const ls = async (options, execOptions = {}) => {
44
43
  return execAsyncSpawn(`docker image ls ${args}`, execOptions);
45
44
  };
46
45
 
46
+ /**
47
+ * @param {import('./image-api').ImagesInspectOptions} options
48
+ * @param {import('../../../util/exec-async-command').ExecAsyncSpawnOptions} execOptions
49
+ */
50
+ const inspect = async (options, execOptions = {}) => {
51
+ const {
52
+ image,
53
+ format,
54
+ formatToJSON = false
55
+ } = options;
56
+
57
+ const formatArg = !formatToJSON && format
58
+ ? `--format=${format}`
59
+ : formatToJSON && '--format=\'{{json .}}\'';
60
+
61
+ const args = [
62
+ formatArg,
63
+ image
64
+ ].filter(Boolean).join(' ');
65
+
66
+ if (formatToJSON) {
67
+ const result = await execAsyncSpawn(`docker image inspect ${args}`, execOptions);
68
+ return JSON.parse(result);
69
+ }
70
+
71
+ return execAsyncSpawn(`docker image inspect ${args}`, execOptions);
72
+ };
73
+
47
74
  module.exports = {
48
- ls
75
+ ls,
76
+ inspect
49
77
  };
@@ -1,5 +1,7 @@
1
1
  const containers = require('./containers');
2
2
  const network = require('./network');
3
+ const volume = require('./volume');
4
+ const dockerApi = require('./api');
3
5
 
4
6
  /**
5
7
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
@@ -19,13 +21,16 @@ const startServices = () => ({
19
21
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
20
22
  */
21
23
  const stopServices = () => ({
24
+ title: 'Stopping Docker services',
22
25
  task: (ctx, task) => task.newListr([
23
26
  containers.stopContainers(),
27
+ volume.removeLocalVolumes(),
24
28
  network.tasks.removeNetwork()
25
29
  ])
26
30
  });
27
31
 
28
32
  module.exports = {
29
33
  startServices,
30
- stopServices
34
+ stopServices,
35
+ dockerApi
31
36
  };
@@ -1,8 +1,10 @@
1
+ const path = require('path');
2
+ const os = require('os');
1
3
  const { DockerFileBuilder } = require('../../util/dockerfile-builder');
2
- const semver = require('semver');
3
4
  const { execAsyncSpawn } = require('../../util/exec-async-command');
4
5
  const KnownError = require('../../errors/known-error');
5
6
  const { runContainerImage } = require('../../util/run-container-image');
7
+ const { imageApi } = require('./image');
6
8
 
7
9
  /**
8
10
  * Get enabled extensions list with versions
@@ -27,15 +29,15 @@ const getEnabledExtensionsFromImage = async (imageWithTag) => {
27
29
  .reduce((acc, [name, version]) => ({ ...acc, [name]: version }), {});
28
30
  };
29
31
 
30
- const addExtensionToBuilder = (builder, ctx) => ([extensionName, extensionInstructions]) => {
32
+ const addExtensionToBuilder = (builder, ctx) => async ([extensionName, extensionInstructions]) => {
31
33
  const { command, ...extensionInstructionsWithoutCommand } = extensionInstructions;
32
34
  let runCommand = '';
33
35
  if (typeof command === 'string') {
34
36
  runCommand += ` ${command}`;
35
- } else if (typeof command === 'function') {
36
- runCommand += ` ${command({ ...extensionInstructionsWithoutCommand, ctx })}`;
37
+ } else if (typeof command === 'function' || command instanceof Promise) {
38
+ runCommand += ` ${await Promise.resolve(command({ ...extensionInstructionsWithoutCommand, ctx }))}`;
37
39
  } else {
38
- runCommand += ` docker-php-ext-install ${extensionInstructionsWithoutCommand.name}`;
40
+ runCommand += ` docker-php-ext-install ${extensionInstructionsWithoutCommand.name || extensionName}`;
39
41
  }
40
42
  builder
41
43
  .comment(`extension ${extensionName} installation command`)
@@ -49,6 +51,7 @@ const addExtensionToBuilder = (builder, ctx) => ([extensionName, extensionInstru
49
51
  const buildDockerFileInstructions = async (ctx, { image, tag }) => {
50
52
  const { composer } = ctx.config.overridenConfiguration.configuration;
51
53
  const existingPHPExtensions = await getEnabledExtensionsFromImage(`${image}:${tag}`);
54
+ const imageDetails = await imageApi.inspect({ image: `${image}:${tag}`, formatToJSON: true });
52
55
 
53
56
  const missingExtensions = Object.entries(
54
57
  ctx.config.overridenConfiguration.configuration.php.extensions
@@ -68,10 +71,6 @@ const buildDockerFileInstructions = async (ctx, { image, tag }) => {
68
71
  .comment('project image')
69
72
  .from({ image, tag });
70
73
 
71
- // install bash in image
72
- dockerFileInstructions
73
- .run('apk add --no-cache bash');
74
-
75
74
  if (missingExtensions.length > 0) {
76
75
  const allDependencies = missingExtensions.map(
77
76
  ([_extensionName, extensionInstructions]) => (extensionInstructions.dependencies || [])
@@ -79,7 +78,9 @@ const buildDockerFileInstructions = async (ctx, { image, tag }) => {
79
78
  .reduce((acc, val) => acc.concat(val.filter((ex) => !acc.includes(ex))), []);
80
79
 
81
80
  dockerFileInstructions.run(`apk add --no-cache ${allDependencies.join(' ')}`);
82
- missingExtensions.forEach(addExtensionToBuilder(dockerFileInstructions, ctx));
81
+ for (const missingExtensionInstructions of missingExtensions) {
82
+ await addExtensionToBuilder(dockerFileInstructions, ctx)(missingExtensionInstructions);
83
+ }
83
84
  }
84
85
 
85
86
  const composerVersion = /^\d$/.test(composer.version)
@@ -92,17 +93,39 @@ const buildDockerFileInstructions = async (ctx, { image, tag }) => {
92
93
  .comment('make composer executable')
93
94
  .run('chmod +x ./composer')
94
95
  .comment('move composer to bin directory')
95
- .run('mv composer /usr/local/bin/composer');
96
+ .run('mv composer /usr/local/bin/composer')
97
+ .run('mkdir -p /composer/home/cache')
98
+ .env({
99
+ COMPOSER_HOME: '/composer/home',
100
+ COMPOSER_CACHE_DIR: '/composer/home/cache'
101
+ });
102
+
103
+ if (composer.plugins && Object.values(composer.plugins).length > 0) {
104
+ for (const [pluginName, pluginOptions] of Object.entries(composer.plugins)) {
105
+ if (pluginOptions.enabled) {
106
+ dockerFileInstructions
107
+ .comment(`install ${pluginName} composer global package`)
108
+ // eslint-disable-next-line max-len
109
+ .run(`composer global require ${pluginName}${ pluginOptions.options ? ` ${pluginOptions.options}` : '' }${ pluginOptions.options ? ` ${pluginOptions.options}` : ''}`);
110
+ }
111
+ }
112
+ }
96
113
 
97
- if (semver.satisfies(composer.version, '^1')) {
114
+ if (!ctx.isDockerDesktop) {
98
115
  dockerFileInstructions
99
- .comment('install prestissimo composer plugin')
100
- .run('composer global require hirak/prestissimo');
116
+ .run(`chown -R ${os.userInfo().uid}:${os.userInfo().gid} /composer/home`);
101
117
  }
102
118
 
103
119
  dockerFileInstructions
104
120
  .workDir(ctx.config.baseConfig.containerMagentoDir);
105
121
 
122
+ const imagePathEnv = imageDetails.Config.Env.find((env) => env.startsWith('PATH'));
123
+
124
+ dockerFileInstructions
125
+ .env({
126
+ PATH: `${ imagePathEnv.split('=').pop() }:${ path.join(ctx.config.baseConfig.containerMagentoDir, 'bin') }`
127
+ });
128
+
106
129
  return dockerFileInstructions;
107
130
  };
108
131
 
@@ -0,0 +1,5 @@
1
+ const systemApi = require('./system-api');
2
+
3
+ module.exports = {
4
+ systemApi
5
+ };