@scandipwa/magento-scripts 1.13.5-alpha.0 → 1.14.1-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 (33) hide show
  1. package/lib/commands/start.js +23 -12
  2. package/lib/config/templates/php-fpm.template.conf +1 -0
  3. package/lib/config/versions/magento-2.3.0.js +3 -1
  4. package/lib/config/versions/magento-2.3.1.js +3 -1
  5. package/lib/config/versions/magento-2.3.2-p2.js +3 -1
  6. package/lib/config/versions/magento-2.3.2.js +3 -1
  7. package/lib/config/versions/magento-2.3.3-p1.js +3 -1
  8. package/lib/config/versions/magento-2.3.3.js +3 -1
  9. package/lib/config/versions/magento-2.3.4-p2.js +3 -1
  10. package/lib/config/versions/magento-2.3.4.js +3 -1
  11. package/lib/config/versions/magento-2.3.5-p1.js +3 -1
  12. package/lib/config/versions/magento-2.3.5-p2.js +3 -1
  13. package/lib/config/versions/magento-2.3.5.js +3 -1
  14. package/lib/config/versions/magento-2.3.6-p1.js +3 -1
  15. package/lib/config/versions/magento-2.3.6.js +3 -1
  16. package/lib/config/versions/magento-2.3.7-p2.js +47 -0
  17. package/lib/config/versions/magento-2.3.7-p3.js +47 -0
  18. package/lib/config/versions/magento-2.4.3-p2.js +51 -0
  19. package/lib/config/versions/magento-2.4.4.js +51 -0
  20. package/lib/tasks/composer/index.js +1 -1
  21. package/lib/tasks/docker/containers.js +16 -4
  22. package/lib/tasks/magento/setup-magento/set-base-url.js +2 -1
  23. package/lib/tasks/php/compile-options.js +4 -3
  24. package/lib/tasks/php/compile.js +1 -1
  25. package/lib/tasks/php/index.js +22 -8
  26. package/lib/tasks/requirements/composer.js +211 -19
  27. package/lib/tasks/requirements/docker/index.js +12 -1
  28. package/lib/tasks/requirements/docker/permissions.js +58 -0
  29. package/lib/tasks/requirements/index.js +1 -2
  30. package/lib/tasks/status/index.js +24 -9
  31. package/lib/util/instance-metadata.js +74 -0
  32. package/package.json +12 -2
  33. package/readme.md +54 -124
@@ -1,36 +1,228 @@
1
+ const path = require('path');
2
+ const fs = require('fs');
3
+ const os = require('os');
1
4
  const logger = require('@scandipwa/scandipwa-dev-utils/logger');
5
+ const pathExists = require('../../util/path-exists');
6
+
7
+ const authJsonPath = path.join(process.cwd(), 'auth.json');
8
+ const shellName = process.env.SHELL.split('/').pop();
9
+ const shellConfigFileName = `.${shellName}rc`;
10
+ const shellConfigFilePath = path.join(os.homedir(), shellConfigFileName);
11
+
12
+ const pasteKeybinding = process.platform === 'darwin' ? 'CMD + V' : 'CTRL + SHIFT + V';
13
+
14
+ const MISSING_COMPOSER_AUTH_ENV = 'missing composer auth environment variable';
15
+ const MISSING_AUTH_JSON = 'missing auth.json file';
2
16
 
3
17
  /**
4
18
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
5
19
  */
6
- const checkComposer = () => ({
7
- title: 'Checking Composer environmental variables',
8
- task: () => {
9
- try {
10
- if (!process.env.COMPOSER_AUTH) {
11
- throw new Error(`Environmental variable ${ logger.style.misc('COMPOSER_AUTH') } is not set.`);
20
+ const configureComposerCredentials = () => ({
21
+ title: 'Configuring Composer Credentials',
22
+ task: async (ctx, task) => {
23
+ const configureLocation = await task.prompt({
24
+ type: 'Select',
25
+ message: `We didn't find Composer credentials in your ${ logger.style.misc('$COMPOSER_AUTH') } environment variable or in your ${ logger.style.file('./auth.json') } file.
26
+
27
+ Those credentials are required to install Magento.
28
+
29
+ Where do you want to store them?`,
30
+ choices: [
31
+ {
32
+ message: `Inside my projects directory in ${ logger.style.file('auth.json') } file [${ logger.style.misc('Recommended') }]`,
33
+ name: 'auth.json'
34
+ },
35
+ {
36
+ message: `Inside my shells configuration file located in users home directory ${ logger.style.file(shellConfigFileName) } [${ logger.style.misc('Shared credentials, not recommended') }] `,
37
+ name: 'shell'
38
+ }
39
+ ]
40
+ });
41
+
42
+ const usernameCredentials = await task.prompt({
43
+ type: 'Input',
44
+ message: `Please enter your username key.
45
+
46
+ You can obtain this key from Magento Marketplace
47
+
48
+ 1. Go to ${ logger.style.link('https://marketplace.magento.com/customer/accessKeys/') }
49
+ 2. Use created Access Keypair or click ${ logger.style.misc('Generate Access Keypair') } to generate new Access Keypair
50
+ 3. Click ${ logger.style.misc('Copy') } on ${ logger.style.misc('Public key') } and paste it here (${logger.style.misc(pasteKeybinding)})
51
+
52
+ ${ logger.style.misc('Username') } (${ logger.style.misc('Public key') }):`,
53
+ required: true,
54
+ validate: (value) => {
55
+ if (value.trim() === '') {
56
+ return 'Username must not be empty';
57
+ }
58
+
59
+ return true;
12
60
  }
61
+ });
13
62
 
14
- let magento;
63
+ const passwordCredentials = await task.prompt({
64
+ type: 'Input',
65
+ message: `Please enter your password key.
15
66
 
16
- try {
17
- magento = JSON.parse(process.env.COMPOSER_AUTH);
18
- } catch (e) {
19
- throw new Error(`Environmental variable ${ logger.style.misc('COMPOSER_AUTH') } is not valid JSON.`);
67
+ You can obtain this key from Magento Marketplace
68
+
69
+ 1. Go to ${ logger.style.link('https://marketplace.magento.com/customer/accessKeys/') }
70
+ 2. Use created Access Keypair or click ${ logger.style.misc('Generate Access Keypair') } to generate new Access Keypair
71
+ 3. Click ${ logger.style.misc('Copy') } on ${ logger.style.misc('Private key') } and paste it here (${logger.style.misc(pasteKeybinding)})
72
+
73
+ ${ logger.style.comment('Make sure to use Private key from the same Keypair as your Public key!')}
74
+
75
+ ${ logger.style.misc('Password') } (${ logger.style.misc('Private key') }):`,
76
+ required: true,
77
+ validate: (value) => {
78
+ if (value.trim() === '') {
79
+ return 'Password must not be empty';
80
+ }
81
+
82
+ return true;
20
83
  }
84
+ });
21
85
 
22
- if (!magento || !magento['http-basic'] || !magento['http-basic']['repo.magento.com']) {
23
- throw new Error(`Environmental variable ${ logger.style.misc('COMPOSER_AUTH') } does not contain the ${ logger.style.misc('repo.magento.com') } field.`);
86
+ const authContent = {
87
+ 'http-basic': {
88
+ 'repo.magento.com': {
89
+ username: usernameCredentials,
90
+ password: passwordCredentials
91
+ }
24
92
  }
25
- } catch (e) {
93
+ };
94
+
95
+ const authJsonContent = JSON.stringify(authContent, null, 4);
96
+ const authEnvContent = `export COMPOSER_AUTH='${JSON.stringify(authContent)}'`;
97
+
98
+ process.env.COMPOSER_AUTH = authJsonContent;
99
+
100
+ if (configureLocation === 'auth.json') {
101
+ await fs.promises.writeFile(authJsonPath, authJsonContent, 'utf-8');
102
+ return;
103
+ }
104
+
105
+ switch (shellName) {
106
+ case 'bash':
107
+ case 'zsh': {
108
+ await fs.promises.appendFile(
109
+ path.join(os.homedir(), shellConfigFileName),
110
+ `\n${authEnvContent}\n`
111
+ );
112
+ break;
113
+ }
114
+ default: {
26
115
  throw new Error(
27
- `To generate Composer credentials login into Magento Marketplace and follow the official guide.
28
- The guide is found here: ${ logger.style.link('https://devdocs.magento.com/guides/v2.3/install-gde/prereq/connect-auth.html') }
29
- Then, insert obtained credentials into this command, and execute:
30
- ${logger.style.code('export COMPOSER_AUTH=\'{"http-basic":{"repo.magento.com": {"username": "<PUBLIC KEY FROM MAGENTO MARKETPLACE>", "password": "<PRIVATE KEY FROM MAGENTO MARKETPLACE>"}}}\'')}\n\n${e}`
116
+ `Unfortunately we cannot automatically add credentials for your shell ${process.env.SHELL}!
117
+
118
+ You will need to that manually!
119
+
120
+ Add following string to your shell configuration file: ${ logger.style.code()}`
31
121
  );
32
122
  }
123
+ }
124
+
125
+ await task.prompt({
126
+ type: 'Confirm',
127
+ message: `Before we continue with installation...
128
+
129
+ You selected to add your Magento credentials to your shell environment, you should know that shell configuration does not update automatically.
130
+
131
+ To update it you can either restart your existing shell or run the following command to update the configuration temporarily: ${ logger.style.code('source <path/to/config/file>')}
132
+
133
+ Press ${ logger.style.misc('Y') } to continue`
134
+ });
135
+ }
136
+ });
137
+
138
+ /**
139
+ * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
140
+ */
141
+ const checkComposerCredentials = () => ({
142
+ title: 'Checking Composer credentials',
143
+ task: async (ctx, task) => {
144
+ const problems = new Map();
145
+ if (!process.env.COMPOSER_AUTH) {
146
+ problems.set(MISSING_COMPOSER_AUTH_ENV, true);
147
+ }
148
+
149
+ if (!await pathExists(authJsonPath)) {
150
+ problems.set(MISSING_AUTH_JSON, true);
151
+ }
152
+
153
+ if (problems.has(MISSING_COMPOSER_AUTH_ENV) && problems.has(MISSING_AUTH_JSON)) {
154
+ let doConfigure = true;
155
+ if (await pathExists(shellConfigFilePath)) {
156
+ const rcFileContent = await fs.promises.readFile(shellConfigFilePath, 'utf-8');
157
+
158
+ const lines = rcFileContent.split('\n');
159
+ const composerAuthInRcFile = lines.some((line) => line.startsWith('export COMPOSER_AUTH='));
160
+
161
+ if (composerAuthInRcFile) {
162
+ doConfigure = false;
163
+ const loadCredentialsFrom = await task.prompt({
164
+ type: 'Confirm',
165
+ message: `We detected that you have ${ logger.style.misc('COMPOSER_AUTH') } environment variable set in ${ logger.style.file(shellConfigFilePath) } file,
166
+ but we do not see this variable inside ${ logger.style.code('magento-scripts') } process.
167
+
168
+ ${ logger.style.misc('! Don\'t forget to reload your shell after process is finished !') }
169
+
170
+ Would you like to load them now?`
171
+ });
172
+
173
+ if (loadCredentialsFrom) {
174
+ const credentialsLine = lines.find((line) => line.startsWith('export COMPOSER_AUTH='));
175
+ process.env.COMPOSER_AUTH = credentialsLine.replace('export COMPOSER_AUTH=', '').replace(/'/ig, '').trim();
176
+ problems.delete(MISSING_COMPOSER_AUTH_ENV);
177
+ }
178
+ }
179
+ }
180
+
181
+ if (doConfigure) {
182
+ return task.newListr(
183
+ configureComposerCredentials()
184
+ );
185
+ }
186
+ }
187
+
188
+ let composerAuthContent;
189
+
190
+ if (!problems.has(MISSING_AUTH_JSON)) {
191
+ try {
192
+ const composerAuthFileContent = await fs.promises.readFile(authJsonPath, 'utf-8');
193
+
194
+ composerAuthContent = JSON.parse(composerAuthFileContent);
195
+
196
+ process.env.COMPOSER_AUTH = composerAuthFileContent;
197
+ } catch (e) {
198
+ throw new Error(
199
+ `We found an error in your ${ logger.style.file('./auth.json') } file.
200
+
201
+ Make sure that this file contains a valid JSON!
202
+
203
+ You can try linting your file here: ${ logger.style.link('https://jsonformatter.curiousconcept.com/') }
204
+
205
+ Error message that we got: ${e}`
206
+ );
207
+ }
208
+ }
209
+
210
+ if (!problems.has(MISSING_COMPOSER_AUTH_ENV) && !composerAuthContent) {
211
+ try {
212
+ composerAuthContent = JSON.parse(process.env.COMPOSER_AUTH);
213
+ } catch (e) {
214
+ throw new Error(
215
+ `We found an error in your ${ logger.style.misc('$COMPOSER_AUTH') } environment variable.
216
+
217
+ Make sure that this variable contains a valid JSON!
218
+
219
+ You can try linting your variable here: ${ logger.style.link('https://jsonformatter.curiousconcept.com/') }
220
+
221
+ Error message that we got: ${e}`
222
+ );
223
+ }
224
+ }
33
225
  }
34
226
  });
35
227
 
36
- module.exports = checkComposer;
228
+ module.exports = checkComposerCredentials;
@@ -3,6 +3,7 @@ const os = require('os');
3
3
  const { execAsyncSpawn } = require('../../../util/exec-async-command');
4
4
  const getIsWsl = require('../../../util/is-wsl');
5
5
  const installDocker = require('./install');
6
+ const { checkDockerSocketPermissions } = require('./permissions');
6
7
  const getDockerVersion = require('./version');
7
8
 
8
9
  /**
@@ -70,4 +71,14 @@ ${ logger.style.link('https://docs.create-magento-app.com/getting-started/prereq
70
71
  }
71
72
  });
72
73
 
73
- module.exports = checkDocker;
74
+ /**
75
+ * @type {() => import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
76
+ */
77
+ module.exports = () => ({
78
+ task: (ctx, task) => task.newListr([
79
+ checkDockerSocketPermissions(),
80
+ checkDocker()
81
+ ], {
82
+ concurrent: false
83
+ })
84
+ });
@@ -0,0 +1,58 @@
1
+ const os = require('os');
2
+ const fs = require('fs');
3
+ const logger = require('@scandipwa/scandipwa-dev-utils/logger');
4
+ const pathExists = require('../../../util/path-exists');
5
+ const { execCommandTask } = require('../../../util/exec-async-command');
6
+
7
+ const dockerSocketPath = '/var/run/docker.sock';
8
+
9
+ const fixCommand = `sudo chmod 666 ${ dockerSocketPath }`;
10
+
11
+ /**
12
+ * @type {() => import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
13
+ */
14
+ const checkDockerSocketPermissions = () => ({
15
+ title: 'Checking Docker permissions',
16
+ // skip check if socket does not exist
17
+ skip: async () => !await pathExists(dockerSocketPath),
18
+ task: async (ctx, task) => {
19
+ try {
20
+ await fs.promises.access(dockerSocketPath, fs.constants.R_OK);
21
+ } catch (e) {
22
+ // check for permission
23
+ if (Math.abs(e.errno) === Math.abs(os.constants.errno.EACCES)) {
24
+ const confirmPrompt = await task.prompt({
25
+ type: 'Confirm',
26
+ message: `We detected that your Docker socket, located in ${ logger.style.file(dockerSocketPath) }, have permissions set, that prevents user (${ logger.style.misc(os.userInfo().username) }) from accessing it.
27
+
28
+ We can fix it by running the following command: ${ logger.style.command(fixCommand) }
29
+
30
+ Would you like to fix this permission issue?
31
+
32
+ Otherwise installation will likely fail.`
33
+ });
34
+
35
+ if (confirmPrompt) {
36
+ task.output = 'Enter your sudo password!';
37
+ task.output = logger.style.command(`>[sudo] password for ${ os.userInfo().username }:`);
38
+ return task.newListr(
39
+ execCommandTask(fixCommand, {
40
+ callback: (t) => {
41
+ task.output = t;
42
+ },
43
+ pipeInput: true
44
+ })
45
+ );
46
+ }
47
+ task.skip(`Permission issue detected in ${ logger.style.file(dockerSocketPath) } but user decided not to fix it.`);
48
+ }
49
+ }
50
+ },
51
+ options: {
52
+ bottomBar: 10
53
+ }
54
+ });
55
+
56
+ module.exports = {
57
+ checkDockerSocketPermissions
58
+ };
@@ -4,7 +4,6 @@ const checkComposer = require('./composer');
4
4
  const checkDocker = require('./docker');
5
5
  const checkNodeVersion = require('./node-version');
6
6
  const checkPHPVersion = require('./php-version');
7
- const localAuthJson = require('../composer/local-auth-json');
8
7
 
9
8
  /**
10
9
  * @type {() => import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
@@ -21,7 +20,7 @@ const checkRequirements = () => ({
21
20
  // check the Docker installation
22
21
  checkDocker(),
23
22
  // check for COMPOSER_AUTH or auth.json
24
- localAuthJson(),
23
+ // localAuthJson(),
25
24
  checkComposer(),
26
25
  // check for Node.js version
27
26
  checkNodeVersion()
@@ -7,6 +7,20 @@ const { getArchSync } = require('../../util/arch');
7
7
  const ConsoleBlock = require('../../util/console-block');
8
8
  const { getComposerVersion } = require('../composer');
9
9
 
10
+ /**
11
+ * @param {string} port
12
+ * @return {{ host: string, hostPort: string, containerPort: string }}
13
+ */
14
+ const parsePort = (port) => {
15
+ const [host, hostPort, containerPort] = port.split(':');
16
+
17
+ return {
18
+ host,
19
+ hostPort,
20
+ containerPort
21
+ };
22
+ };
23
+
10
24
  const prettyStatus = async (ctx) => {
11
25
  const {
12
26
  ports,
@@ -64,14 +78,11 @@ const prettyStatus = async (ctx) => {
64
78
  .addEmptyLine();
65
79
 
66
80
  let containerStatus;
67
- if (container.status) {
68
- if (container.status.Status === 'healthy') {
69
- containerStatus = `✓ ${logger.style.file('running')}`;
70
- } else {
71
- containerStatus = logger.style.code(container.status.Status);
72
- }
81
+
82
+ if (container.status && container.status.Health) {
83
+ containerStatus = `✓ ${ logger.style.file(container.status.Health.Status) } and ${ logger.style.file('running') }`;
73
84
  } else {
74
- containerStatus = logger.style.code('stopped');
85
+ containerStatus = logger.style.file(container.status.Status);
75
86
  }
76
87
 
77
88
  block
@@ -81,10 +92,14 @@ const prettyStatus = async (ctx) => {
81
92
  .addLine(`Network: ${logger.style.link(container.network)}`);
82
93
 
83
94
  if (container.ports.length > 0) {
84
- block.addLine(`Port forwarding: ${container.ports.map((port) => logger.style.link(port)).join(', ')}`);
95
+ block.addLine('Port forwarding:');
96
+ container.ports.forEach((port) => {
97
+ const { host, hostPort, containerPort } = parsePort(port);
98
+ block.addLine(`${' '.repeat(3)} ${logger.style.link(`${host}:${hostPort}`)} -> ${logger.style.file(containerPort)}`);
99
+ });
85
100
  }
86
101
 
87
- if (container.env) {
102
+ if (container.env && Object.keys(container.env).length > 0) {
88
103
  block.addLine('Environment variables:');
89
104
  for (const [envName, envValue] of Object.entries(container.env)) {
90
105
  block.addLine(`${' '.repeat(3)} ${logger.style.misc(envName)}=${logger.style.file(envValue)}`);
@@ -0,0 +1,74 @@
1
+ const logger = require('@scandipwa/scandipwa-dev-utils/logger');
2
+
3
+ const WEB_LOCAL_LOCATION_TITLE = `Location on ${ logger.style.misc('localhost') }`;
4
+ const WEB_LOCATION_TITLE = 'Location on the Web';
5
+ const WEB_ADMIN_LOCATION_TITLE = 'Panel location';
6
+ const WEB_ADMIN_CREDENTIALS_TITLE = 'Panel credentials';
7
+
8
+ const mapDataStyle = ({ title, text }) => ({
9
+ title,
10
+ text: logger.style.link(text)
11
+ });
12
+
13
+ /**
14
+ * @param {import("../../typings/context").ListrContext} ctx
15
+ * @return {{ frontend: { title: string, text: string }[], admin: { title: string, url: string }[]}}
16
+ */
17
+ const getInstanceMetadata = (ctx) => {
18
+ const {
19
+ ports,
20
+ config: {
21
+ magentoConfiguration,
22
+ overridenConfiguration: { host, ssl }
23
+ }
24
+ } = ctx;
25
+
26
+ /**
27
+ * @type {{ title: string, text: string }[]}
28
+ */
29
+ const frontend = [];
30
+
31
+ /**
32
+ * @type {{ title: string, text: string }[]}
33
+ */
34
+ const admin = [];
35
+
36
+ const isNgrok = host.endsWith('ngrok.io');
37
+
38
+ if (isNgrok) {
39
+ frontend.push({
40
+ title: WEB_LOCAL_LOCATION_TITLE,
41
+ text: `${ssl.enabled ? 'https' : 'http'}://localhost${ssl.enabled || ports.app === 80 ? '' : `:${ports.app}`}/`
42
+ });
43
+ frontend.push({
44
+ title: WEB_LOCATION_TITLE,
45
+ text: `${ssl.enabled ? 'https' : 'http'}://${host}/`
46
+ });
47
+ } else {
48
+ frontend.push({
49
+ title: WEB_LOCATION_TITLE,
50
+ text: `${ssl.enabled ? 'https' : 'http'}://${host}${ssl.enabled || ports.app === 80 ? '' : `:${ports.app}`}/`
51
+ });
52
+ }
53
+
54
+ const webLocation = frontend.find((u) => u.title === WEB_LOCATION_TITLE);
55
+
56
+ admin.push({
57
+ title: WEB_ADMIN_LOCATION_TITLE,
58
+ text: logger.style.link(`${webLocation.text}admin`)
59
+ });
60
+
61
+ admin.push({
62
+ title: WEB_ADMIN_CREDENTIALS_TITLE,
63
+ text: `${logger.style.misc(magentoConfiguration.user)} - ${logger.style.misc(magentoConfiguration.password)}`
64
+ });
65
+
66
+ return {
67
+ admin,
68
+ frontend: frontend.map(mapDataStyle)
69
+ };
70
+ };
71
+
72
+ module.exports = {
73
+ getInstanceMetadata
74
+ };
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": "1.13.5-alpha.0",
6
+ "version": "1.14.1-alpha.0",
7
7
  "main": "./index.js",
8
8
  "types": "./typings/index.d.ts",
9
9
  "license": "OSL-3.0",
@@ -41,5 +41,15 @@
41
41
  "publishConfig": {
42
42
  "access": "public"
43
43
  },
44
- "gitHead": "d441da5928b8f8cf0fb6034ffe09ce186a0370d6"
44
+ "keywords": [
45
+ "magento",
46
+ "docker",
47
+ "php",
48
+ "nginx",
49
+ "redis",
50
+ "elasticsearch",
51
+ "mysql",
52
+ "scandipwa"
53
+ ],
54
+ "gitHead": "055ef1bd01035af8eaf80dffa087e77c67938fa8"
45
55
  }