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

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.
@@ -1,6 +1,8 @@
1
1
  const logger = require('@scandipwa/scandipwa-dev-utils/logger')
2
2
  const { Listr } = require('listr2')
3
3
  const importDump = require('../tasks/import-dump')
4
+ const { getInstanceMetadata } = require('../util/instance-metadata')
5
+ const ConsoleBlock = require('../util/console-block')
4
6
 
5
7
  /**
6
8
  * @param {import('yargs')} yargs
@@ -37,14 +39,45 @@ module.exports = (yargs) => {
37
39
  }
38
40
  })
39
41
 
40
- try {
41
- await tasks.run()
42
+ /**
43
+ * @type {import('../../typings/context').ListrContext}
44
+ */
45
+ let ctx
42
46
 
43
- process.exit(0)
47
+ try {
48
+ ctx = await tasks.run()
44
49
  } catch (e) {
45
50
  logger.error(e.message || e)
46
51
  process.exit(1)
47
52
  }
53
+
54
+ const instanceMetadata = getInstanceMetadata(ctx)
55
+
56
+ const block = new ConsoleBlock()
57
+ block.addHeader('Magento 2').addEmptyLine()
58
+
59
+ block.addLine(logger.style.misc('Frontend'))
60
+ instanceMetadata.frontend.forEach(({ title, text }) => {
61
+ block.addLine(` ${title}: ${text}`)
62
+ })
63
+
64
+ block.addEmptyLine()
65
+
66
+ block.addLine(logger.style.misc('Admin'))
67
+ instanceMetadata.admin.forEach(({ title, text }) => {
68
+ block.addLine(` ${title}: ${text}`)
69
+ })
70
+
71
+ block.addEmptyLine()
72
+
73
+ block.addLine(logger.style.misc('MailDev'))
74
+ instanceMetadata.maildev.forEach(({ title, text }) => {
75
+ block.addLine(` ${title}: ${text}`)
76
+ })
77
+
78
+ block.log()
79
+
80
+ process.exit(0)
48
81
  }
49
82
  )
50
83
  }
@@ -4,10 +4,9 @@ log_level = debug
4
4
 
5
5
  [www]
6
6
  clear_env = no
7
- user = www-data
8
- ;; Commented because if it is not set, php-fpm will use default group.
9
- ;; https://www.php.net/manual/en/install.fpm.configuration.php#group
10
- ; group = nobody
7
+ user = <%= it.user %>
8
+
9
+ group = <%= it.user %>
11
10
 
12
11
  catch_workers_output = yes
13
12
 
@@ -2,6 +2,7 @@ const configureElasticsearch = require('../magento/setup-magento/configure-searc
2
2
  const deleteAdminUsers = require('../magento/setup-magento/delete-admin-users')
3
3
  const deleteCustomers = require('../magento/setup-magento/delete-customers')
4
4
  const deleteOrders = require('../magento/setup-magento/delete-orders')
5
+ const disableCustomAdminPath = require('../magento/setup-magento/disable-custom-admin-path')
5
6
  const varnishConfigSetup = require('../magento/setup-magento/varnish-config')
6
7
 
7
8
  /**
@@ -30,6 +31,7 @@ const fixDB = () => ({
30
31
  [
31
32
  varnishConfigSetup(),
32
33
  configureElasticsearch(),
34
+ disableCustomAdminPath(),
33
35
  {
34
36
  title: 'Deleting customers data',
35
37
  skip: ({ withCustomersData }) => withCustomersData,
@@ -39,14 +39,16 @@ const copyDatabaseDumpIntoContainer = () => ({
39
39
  const runSetGlobalLogBinTrustFunctionCreatorsCommand = () => ({
40
40
  task: async (ctx, task) => {
41
41
  const {
42
- config: { docker },
42
+ config: { docker, overridenConfiguration },
43
43
  ports
44
44
  } = ctx
45
45
  const { mariadb } = docker.getContainers(ports)
46
46
 
47
+ const { binFileName } = overridenConfiguration.configuration.mariadb
48
+
47
49
  return task.newListr(
48
50
  execCommandTask(
49
- `docker exec ${mariadb.name} bash -c 'mysql -uroot -p${mariadb.env.MARIADB_ROOT_PASSWORD} -e "SET GLOBAL log_bin_trust_function_creators = 1;"'`
51
+ `docker exec ${mariadb.name} bash -c '${binFileName} -uroot -p${mariadb.env.MARIADB_ROOT_PASSWORD} -e "SET GLOBAL log_bin_trust_function_creators = 1;"'`
50
52
  )
51
53
  )
52
54
  }
@@ -95,10 +97,11 @@ Note that you will lose your existing database!`,
95
97
  const executeImportDumpSQL = () => ({
96
98
  task: async (ctx, task) => {
97
99
  const {
98
- config: { docker },
100
+ config: { docker, overridenConfiguration },
99
101
  ports
100
102
  } = ctx
101
103
  const { mariadb } = docker.getContainers(ports)
104
+ const { binFileName } = overridenConfiguration.configuration.mariadb
102
105
 
103
106
  const userCredentialsForMariaDBCLI = await task.prompt({
104
107
  type: 'Select',
@@ -117,7 +120,7 @@ const executeImportDumpSQL = () => ({
117
120
  ]
118
121
  })
119
122
 
120
- const importCommand = `docker exec ${mariadb.name} bash -c "mysql ${userCredentialsForMariaDBCLI} magento < ./dump.sql"`
123
+ const importCommand = `docker exec ${mariadb.name} bash -c "${binFileName} ${userCredentialsForMariaDBCLI} magento < ./dump.sql"`
121
124
 
122
125
  const startImportTime = Date.now()
123
126
  const tickInterval = setInterval(() => {
@@ -169,9 +169,12 @@ const buildDockerFileInstructions = async (
169
169
  }
170
170
  }
171
171
 
172
- if (!ctx.isDockerDesktop) {
173
- const { uid, gid } = os.userInfo()
174
- dockerFileInstructions.run(`chown -R ${uid}:${gid} /composer/home`)
172
+ if (ctx.platform === 'linux') {
173
+ const { gid, username } = os.userInfo()
174
+ dockerFileInstructions.run(
175
+ `addgroup -g ${gid} ${username} && adduser -u ${gid} -G ${username} -H -s /sbin/nologin -D ${username} && \
176
+ addgroup www-data ${username}`
177
+ )
175
178
  }
176
179
 
177
180
  dockerFileInstructions.workDir(ctx.config.baseConfig.containerMagentoDir)
@@ -1,3 +1,4 @@
1
+ const os = require('os')
1
2
  const UnknownError = require('../../errors/unknown-error')
2
3
  const setConfigFile = require('../../util/set-config')
3
4
 
@@ -13,13 +14,17 @@ const createPhpFpmConfig = () => ({
13
14
  } = ctx
14
15
  const port = !isDockerDesktop ? ctx.ports.fpm : 9000
15
16
 
17
+ const user =
18
+ ctx.platform === 'linux' ? os.userInfo().username : 'www-data'
19
+
16
20
  try {
17
21
  await setConfigFile({
18
22
  configPathname: php.fpmConfPath,
19
23
  template: php.fpmTemplatePath,
20
24
  overwrite: true,
21
25
  templateArgs: {
22
- port
26
+ port,
27
+ user
23
28
  }
24
29
  })
25
30
  } catch (e) {
@@ -1,3 +1,4 @@
1
+ const os = require('os')
1
2
  const UnknownError = require('../../errors/unknown-error')
2
3
  const setConfigFile = require('../../util/set-config')
3
4
 
@@ -13,13 +14,17 @@ const createPhpFpmDebugConfig = () => ({
13
14
  } = ctx
14
15
  const port = !isDockerDesktop ? ctx.ports.fpmXdebug : 9000
15
16
 
17
+ const user =
18
+ ctx.platform === 'linux' ? os.userInfo().username : 'www-data'
19
+
16
20
  try {
17
21
  await setConfigFile({
18
22
  configPathname: php.debugFpmConfPath,
19
23
  template: php.fpmTemplatePath,
20
24
  overwrite: true,
21
25
  templateArgs: {
22
- port
26
+ port,
27
+ user
23
28
  }
24
29
  })
25
30
  } catch (e) {
@@ -160,6 +160,46 @@ mv ${tempDir}/composer.json ${
160
160
  )
161
161
  }
162
162
 
163
+ /**
164
+ * Will check if the following conditions are met:
165
+ * - composer.lock file exists
166
+ * - vendor directory exists
167
+ * - all packages from composer.lock are installed in vendor directory
168
+ * @param {string} magentoDir
169
+ */
170
+ const getIsVendorFolderCorrupted = async (magentoDir) => {
171
+ const composerLockFile = path.join(magentoDir, 'composer.lock')
172
+ const vendorDir = path.join(magentoDir, 'vendor')
173
+ const [vendorDirStat, composerLockFileStat] = await Promise.all([
174
+ pathExists(vendorDir),
175
+ pathExists(composerLockFile)
176
+ ])
177
+ if (!vendorDirStat || !composerLockFileStat) {
178
+ return true
179
+ }
180
+ /**
181
+ * @type {{ packages: { name: string }[] } | null}
182
+ */
183
+ const composerLockData = await getJsonFileData(composerLockFile)
184
+ if (!composerLockData || !composerLockData.packages) {
185
+ return true
186
+ }
187
+ const { packages } = composerLockData
188
+ const packagesNames = packages.map((pkg) => pkg.name)
189
+
190
+ const hasCorruptPackages = await Promise.all(
191
+ packagesNames.map(async (pkg) => {
192
+ const vendorPackage = path.join(vendorDir, pkg)
193
+ const composerJson = path.join(vendorPackage, 'composer.json')
194
+ if (!(await pathExists(composerJson))) {
195
+ return true
196
+ }
197
+ })
198
+ )
199
+
200
+ return hasCorruptPackages.every((result) => result === true)
201
+ }
202
+
163
203
  /**
164
204
  * @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
165
205
  */
@@ -197,7 +237,11 @@ const installMagentoProject = () => ({
197
237
  'composer.lock': true
198
238
  })
199
239
 
200
- if (isFsMatching) {
240
+ const isVendorFolderCorrupted = await getIsVendorFolderCorrupted(
241
+ baseConfig.magentoDir
242
+ )
243
+
244
+ if (isFsMatching && !isVendorFolderCorrupted) {
201
245
  ctx.magentoFirstInstall = false
202
246
  task.skip()
203
247
  return
@@ -206,17 +250,24 @@ const installMagentoProject = () => ({
206
250
  task.title = `Installing Magento ${magentoPackageVersion}`
207
251
  task.output = `Creating Magento ${magentoPackageVersion} project`
208
252
 
209
- if (!(await pathExists(path.join(process.cwd(), 'composer.json')))) {
210
- await createMagentoProject(ctx, task, {
211
- magentoProject,
212
- magentoPackageVersion
213
- })
214
- }
253
+ if (!isFsMatching) {
254
+ if (
255
+ !(await pathExists(path.join(process.cwd(), 'composer.json')))
256
+ ) {
257
+ await createMagentoProject(ctx, task, {
258
+ magentoProject,
259
+ magentoPackageVersion
260
+ })
261
+ }
215
262
 
216
- if (!(await pathExists(path.join(process.cwd(), 'app', 'etc')))) {
217
- await fs.promises.mkdir(path.join(process.cwd(), 'app', 'etc'), {
218
- recursive: true
219
- })
263
+ if (!(await pathExists(path.join(process.cwd(), 'app', 'etc')))) {
264
+ await fs.promises.mkdir(
265
+ path.join(process.cwd(), 'app', 'etc'),
266
+ {
267
+ recursive: true
268
+ }
269
+ )
270
+ }
220
271
  }
221
272
 
222
273
  return task.newListr([
@@ -226,7 +277,7 @@ const installMagentoProject = () => ({
226
277
  title: 'Installing Magento dependencies',
227
278
  task: async () => {
228
279
  try {
229
- await runComposerCommand(ctx, 'install', {
280
+ await runComposerCommand(ctx, 'install -vvv', {
230
281
  callback: !ctx.verbose
231
282
  ? undefined
232
283
  : (t) => {
@@ -1,18 +1,12 @@
1
1
  <?php
2
- $directories = [
3
- '/var',
4
- '/generated',
5
- '/vendor',
6
- '/pub/static',
7
- '/pub/media',
8
- '/app/etc',
9
- ];
2
+ // receive directories from command line arguments
3
+ $directories = explode(",", $argv[1]);
10
4
 
11
5
  $results = [];
12
6
 
13
- foreach ($directories as $dir) {
14
- $directory = getcwd() . $dir;
7
+ foreach ($directories as $directory) {
15
8
  $result = [];
9
+ $result['directory'] = $directory;
16
10
  $result['exists'] = file_exists($directory);
17
11
  $result['writable'] = is_writable($directory);
18
12
  $result['readable'] = is_readable($directory);
@@ -22,11 +16,40 @@ foreach ($directories as $dir) {
22
16
  $result['group'] = posix_getgrgid(filegroup($directory));
23
17
  $result['current_user'] = posix_getpwuid(posix_geteuid());
24
18
 
25
- $result['permissions'] =substr(sprintf('%o', fileperms($directory)), -4);
19
+ // Check if current user is in the group
20
+ if ($result['group']['members'] && is_array($result['group']['members'])) {
21
+ $result['current_user_in_group'] = in_array($result['current_user']['name'], $result['group']['members']);
22
+ } else {
23
+ $result['current_user_in_group'] = false;
24
+ }
25
+
26
+ // Check if the directory has group write permissions (g+w)
27
+ $permissions = fileperms($directory);
28
+ $result['has_group_write_permissions'] = ($permissions & 0x0010) !== 0;
29
+
30
+ $result['permissions'] = substr(sprintf('%o', fileperms($directory)), -4);
26
31
  $result['is_current_user_directory_owner'] = $result['current_user'] === $result['owner'];
32
+
33
+ if ($result['writable']) {
34
+ // Check if new files inherit the directory's group
35
+ try {
36
+ $tempFile = $directory . DIRECTORY_SEPARATOR . '.temp_file_' . uniqid();
37
+ touch($tempFile);
38
+
39
+ $tempFileGroup = posix_getgrgid(filegroup($tempFile));
40
+ $result['new_files_inherit_group'] = $tempFileGroup === $result['group'];
41
+ } catch (Exception $e) {
42
+ $result['new_files_inherit_group'] = false;
43
+ } finally {
44
+ // Clean up the temporary file
45
+ if (file_exists($tempFile)) {
46
+ unlink($tempFile);
47
+ }
48
+ }
49
+ }
27
50
  }
28
51
 
29
52
  $results[] = $result;
30
53
  }
31
54
 
32
- echo json_encode($results, JSON_PRETTY_PRINT);
55
+ echo json_encode($results, JSON_PRETTY_PRINT);
@@ -0,0 +1,21 @@
1
+ const { deleteTableValues } = require('../../../util/database')
2
+
3
+ /**
4
+ * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
5
+ */
6
+ const disableCustomAdminPath = () => ({
7
+ title: 'Disabling custom admin path',
8
+ task: async ({ databaseConnection }, task) => {
9
+ await deleteTableValues(
10
+ 'core_config_data',
11
+ [
12
+ {
13
+ path: 'admin/url/use_custom_path'
14
+ }
15
+ ],
16
+ { databaseConnection, task }
17
+ )
18
+ }
19
+ })
20
+
21
+ module.exports = disableCustomAdminPath
@@ -1,3 +1,5 @@
1
+ const path = require('path')
2
+ const pathExists = require('../../../util/path-exists')
1
3
  const runMagentoCommand = require('../../../util/run-magento')
2
4
 
3
5
  /**
@@ -6,6 +8,17 @@ const runMagentoCommand = require('../../../util/run-magento')
6
8
  const disableMaintenanceMode = () => ({
7
9
  title: 'Disabling maintenance mode',
8
10
  task: async (ctx, task) => {
11
+ const maintenanceModeFile = `var/.maintenance.flag`
12
+
13
+ if (
14
+ !(await pathExists(
15
+ path.join(ctx.config.baseConfig.magentoDir, maintenanceModeFile)
16
+ ))
17
+ ) {
18
+ task.skip()
19
+ return
20
+ }
21
+
9
22
  const { result } = await runMagentoCommand(ctx, 'maintenance:status', {
10
23
  throwNonZeroCode: false
11
24
  })
@@ -1,5 +1,5 @@
1
+ const envPhpToJson = require('../../../util/env-php-json')
1
2
  const magentoTask = require('../../../util/magento-task')
2
- const runMagentoCommand = require('../../../util/run-magento')
3
3
 
4
4
  /**
5
5
  * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
@@ -12,11 +12,14 @@ module.exports = () => ({
12
12
  magentoConfiguration: { mode }
13
13
  }
14
14
  } = ctx
15
- const { result } = await runMagentoCommand(ctx, 'deploy:mode:show', {
16
- throwNonZeroCode: false
17
- })
15
+ const envPhpData = await envPhpToJson(ctx)
16
+ if (!envPhpData) {
17
+ task.skip()
18
+ return
19
+ }
20
+ const { MAGE_MODE } = envPhpData
18
21
 
19
- if (result.includes(mode)) {
22
+ if (MAGE_MODE === mode) {
20
23
  task.skip()
21
24
  return
22
25
  }
@@ -10,14 +10,28 @@ const setMailConfig = () => ({
10
10
  'core_config_data',
11
11
  [
12
12
  {
13
- path: 'smtp/configuration_option/port',
13
+ path: 'system/smtp/transport',
14
+ value: 'smtp'
15
+ },
16
+ {
17
+ path: 'system/smtp/host',
18
+ value: isDockerDesktop
19
+ ? 'host.docker.internal'
20
+ : '127.0.0.1'
21
+ },
22
+ {
23
+ path: 'system/smtp/port',
14
24
  value: `${ports.maildevSMTP}`
15
25
  },
16
26
  {
17
27
  path: 'smtp/configuration_option/host',
18
28
  value: isDockerDesktop
19
29
  ? 'host.docker.internal'
20
- : 'localhost'
30
+ : '127.0.0.1'
31
+ },
32
+ {
33
+ path: 'smtp/configuration_option/port',
34
+ value: `${ports.maildevSMTP}`
21
35
  }
22
36
  ],
23
37
  { databaseConnection, task }
@@ -6,33 +6,58 @@ const {
6
6
  runPHPContainerCommand
7
7
  } = require('../../php/php-container')
8
8
 
9
+ const directoriesToCheck = [
10
+ 'var',
11
+ 'generated',
12
+ 'vendor',
13
+ 'pub/media',
14
+ 'pub/static',
15
+ 'app/etc'
16
+ ]
17
+
9
18
  /**
19
+ * @param {string[]} directories
10
20
  * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
11
21
  */
12
- const makeNewFilesCreatedInFolderUseDirectoryGroup = () => ({
22
+ const makeNewFilesCreatedInFolderUseDirectoryGroup = (directories) => ({
13
23
  title: 'Make new files created in folder use directory group',
14
- task: (ctx, task) =>
15
- task.newListr([
24
+ task: async (ctx, task) => {
25
+ if (directories.length === 0) {
26
+ task.skip()
27
+ return
28
+ }
29
+
30
+ return task.newListr([
16
31
  runPHPContainerCommandTask(
17
- 'find var generated vendor pub/static pub/media app/etc -type d -exec chmod g+ws {} +',
32
+ `find ${directories.join(' ')} -type d -exec chmod g+ws {} +`,
18
33
  {
19
34
  // should prevent command from failing the task
20
35
  // if the folder does not exist
21
- withCode: true
36
+ withCode: true,
37
+ useAutomaticUser: false
22
38
  }
23
39
  )
24
40
  ])
41
+ }
25
42
  })
26
43
 
27
44
  /**
45
+ * @param {string[]} directories
28
46
  * @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
29
47
  */
30
- const makeFilesWritableForGroupMembers = () => ({
48
+ const makeFilesWritableForGroupMembers = (directories) => ({
31
49
  title: 'Make files writable for group members',
32
- task: (ctx, task) =>
33
- task.newListr([
50
+ task: async (ctx, task) => {
51
+ if (directories.length === 0) {
52
+ task.skip()
53
+ return
54
+ }
55
+
56
+ return task.newListr([
34
57
  runPHPContainerCommandTask(
35
- 'find var generated vendor pub/static pub/media app/etc -type f -exec chmod g+w {} +',
58
+ `find ${directories.join(
59
+ ' '
60
+ )} -type -type f -exec chmod g+w {} +`,
36
61
  {
37
62
  // should prevent command from failing the task
38
63
  // if the folder does not exist
@@ -40,6 +65,7 @@ const makeFilesWritableForGroupMembers = () => ({
40
65
  }
41
66
  )
42
67
  ])
68
+ }
43
69
  })
44
70
 
45
71
  /**
@@ -65,39 +91,41 @@ const makeFolderOwnedByUser = (folder, user, group) => ({
65
91
 
66
92
  /**
67
93
  * @param {import('../../../../typings/context').ListrContext} ctx
94
+ * @param {string[]} directories
95
+ * @return {Promise<{directory: string, exists: boolean, new_files_inherit_group: boolean, has_group_write_permissions: boolean, writable: boolean, permissions: string, directory_owner: {name: string, passwd: string, uid: number, gid: number, gecos: string, dir: string, shell: string} | boolean, directory_group: boolean, current_user: {name: string, passwd: string, uid: number, gid: number, gecos: string, dir: string, shell: string} | boolean, is_current_user_directory_owner: boolean}[]>}
68
96
  */
69
- const doesFileSystemNeedsPermissionsSetup = async (ctx) => {
97
+ const checkDirectoriesPermissions = async (ctx, directories) => {
70
98
  const checkPHPPermissionsFileName = 'check-file-permissions.php'
71
99
  const cacheDirFilePath = path.join(
72
100
  ctx.config.baseConfig.cacheDir,
73
- checkPHPPermissionsFileName
101
+ `${Date.now()}-${checkPHPPermissionsFileName}`
74
102
  )
75
103
  await fs.promises.copyFile(
76
104
  path.join(__dirname, checkPHPPermissionsFileName),
77
105
  cacheDirFilePath
78
106
  )
107
+ const user =
108
+ ctx.platform === 'linux' ? `${os.userInfo().username}` : 'www-data'
109
+ const group =
110
+ ctx.platform === 'linux' ? `${os.userInfo().username}` : 'www-data'
111
+
112
+ const userWithGroup = `${user}:${group}`
79
113
 
80
114
  const result = await runPHPContainerCommand(
81
115
  ctx,
82
- `php ${path.relative(process.cwd(), cacheDirFilePath)}`,
116
+ `php ${path.relative(
117
+ ctx.config.baseConfig.containerMagentoDir,
118
+ cacheDirFilePath
119
+ )} ${directories.join(',')}`,
83
120
  {
84
- user: 'www-data:www-data',
121
+ user: userWithGroup,
85
122
  cwd: ctx.config.baseConfig.containerMagentoDir
86
123
  }
87
124
  )
88
125
 
89
126
  await fs.promises.unlink(cacheDirFilePath)
90
127
 
91
- /**
92
- * @type {{directory: string, exists: boolean, writable: boolean, permissions: string, directory_owner: {name: string, passwd: string, uid: number, gid: number, gecos: string, dir: string, shell: string} | boolean, directory_group: boolean, current_user: {name: string, passwd: string, uid: number, gid: number, gecos: string, dir: string, shell: string} | boolean, is_current_user_directory_owner: boolean}[]}
93
- */
94
- const parsedResult = JSON.parse(result)
95
-
96
- if (parsedResult.some(({ exists, writable }) => exists && !writable)) {
97
- return true
98
- }
99
-
100
- return false
128
+ return JSON.parse(result)
101
129
  }
102
130
 
103
131
  /**
@@ -105,27 +133,69 @@ const doesFileSystemNeedsPermissionsSetup = async (ctx) => {
105
133
  */
106
134
  const setupMagentoFilePermissions = () => ({
107
135
  title: 'Setting Magento file permissions',
108
- // skip: true,
109
- skip: async (ctx) =>
110
- ctx.magentoFirstInstall ||
111
- !(await doesFileSystemNeedsPermissionsSetup(ctx)),
112
- task: (ctx, task) =>
113
- task.newListr(
114
- [
115
- makeNewFilesCreatedInFolderUseDirectoryGroup(),
116
- makeFilesWritableForGroupMembers()
117
- ].concat(
118
- ctx.isDockerDesktop
119
- ? [
120
- makeFolderOwnedByUser(
121
- ctx.config.baseConfig.containerMagentoDir,
122
- 'www-data',
123
- 'www-data'
124
- )
125
- ]
126
- : []
136
+ task: async (ctx, task) => {
137
+ const parsedResult = await checkDirectoriesPermissions(
138
+ ctx,
139
+ directoriesToCheck.map((directory) =>
140
+ path.join(ctx.config.baseConfig.containerMagentoDir, directory)
127
141
  )
128
142
  )
143
+ const nonWritableDirectories = parsedResult.filter(
144
+ ({ exists, writable }) => exists && !writable
145
+ )
146
+
147
+ const tasks = []
148
+
149
+ if (nonWritableDirectories.length !== 0) {
150
+ const nonWritableDirectoriesPaths = nonWritableDirectories.map(
151
+ ({ directory }) => directory
152
+ )
153
+ const user =
154
+ ctx.platform === 'linux'
155
+ ? `${os.userInfo().username}`
156
+ : 'www-data'
157
+ const group =
158
+ ctx.platform === 'linux'
159
+ ? `${os.userInfo().username}`
160
+ : 'www-data'
161
+
162
+ tasks.push(
163
+ makeFilesWritableForGroupMembers(nonWritableDirectoriesPaths),
164
+ {
165
+ task: (subCtx, subTask) =>
166
+ subTask.newListr(
167
+ nonWritableDirectoriesPaths.map((directory) =>
168
+ makeFolderOwnedByUser(directory, user, group)
169
+ )
170
+ ),
171
+ options: {
172
+ concurrent: true
173
+ }
174
+ }
175
+ )
176
+ }
177
+
178
+ const directoriesThatNeedNewFilesInheritGroup = parsedResult
179
+ .filter(
180
+ ({ exists, new_files_inherit_group: nfig }) => exists && !nfig
181
+ )
182
+ .map(({ directory }) => directory)
183
+
184
+ if (directoriesThatNeedNewFilesInheritGroup.length > 0) {
185
+ tasks.push(
186
+ makeNewFilesCreatedInFolderUseDirectoryGroup(
187
+ directoriesThatNeedNewFilesInheritGroup
188
+ )
189
+ )
190
+ }
191
+
192
+ if (tasks.length === 0) {
193
+ task.skip()
194
+ return
195
+ }
196
+
197
+ return task.newListr(tasks)
198
+ }
129
199
  })
130
200
 
131
201
  /**
@@ -133,23 +203,29 @@ const setupMagentoFilePermissions = () => ({
133
203
  */
134
204
  const setupComposerCachePermissions = () => ({
135
205
  title: 'Setting Composer Cache permissions',
136
- task: (ctx, task) =>
137
- task.newListr([
138
- ctx.isDockerDesktop
139
- ? makeFolderOwnedByUser(
140
- '/composer/home',
141
- 'www-data',
142
- 'www-data'
143
- )
144
- : makeFolderOwnedByUser(
145
- '/composer/home',
146
- os.userInfo().uid,
147
- os.userInfo().gid
148
- ),
149
-
150
- runPHPContainerCommandTask('chmod g+ws /composer/home/cache'),
151
- runPHPContainerCommandTask('chmod g+w /composer/home/cache')
206
+ task: async (ctx, task) => {
207
+ const parsedResult = await checkDirectoriesPermissions(ctx, [
208
+ '/composer/home'
209
+ ])
210
+
211
+ if (parsedResult.every((dir) => dir.writable)) {
212
+ task.skip()
213
+ return
214
+ }
215
+
216
+ const userAndGroup =
217
+ ctx.platform === 'linux' ? `${os.userInfo().username}` : 'www-data'
218
+
219
+ return task.newListr([
220
+ makeFolderOwnedByUser('/composer/home', userAndGroup, userAndGroup),
221
+ runPHPContainerCommandTask('chmod g+ws /composer/home/cache', {
222
+ user: ctx.platform === 'linux' ? 'root:root' : ''
223
+ }),
224
+ runPHPContainerCommandTask('chmod g+w /composer/home/cache', {
225
+ user: ctx.platform === 'linux' ? 'root:root' : ''
226
+ })
152
227
  ])
228
+ }
153
229
  })
154
230
 
155
231
  module.exports = {
@@ -9,6 +9,7 @@ const { containerApi } = require('../docker/containers')
9
9
  * @param {Parameters<typeof import('./php-container')['runPHPContainerCommand']>[2] & { useAutomaticUser?: boolean}} [options]
10
10
  */
11
11
  const runPHPContainerCommand = async (ctx, command, options = {}) => {
12
+ const { useAutomaticUser = true } = options
12
13
  const { php } = ctx.config.docker.getContainers(ctx.ports)
13
14
 
14
15
  const containers = await containerApi.ls({
@@ -29,16 +30,16 @@ const runPHPContainerCommand = async (ctx, command, options = {}) => {
29
30
  rm: true,
30
31
  command
31
32
  },
32
- options.user
33
+ useAutomaticUser && ctx.platform === 'linux'
33
34
  ? {
34
- user: options.user
35
+ user: `${os.userInfo().username}:${
36
+ os.userInfo().username
37
+ }`
35
38
  }
36
39
  : {},
37
- options.useAutomaticUser
40
+ options.user
38
41
  ? {
39
- user: ctx.isDockerDesktop
40
- ? 'www-data:www-data'
41
- : `${os.userInfo().uid}:${os.userInfo().gid}`
42
+ user: options.user
42
43
  }
43
44
  : {}
44
45
  ),
@@ -91,6 +91,7 @@ const prettyStatus = async (ctx) => {
91
91
  .addLine(`Platform: ${logger.style.code(platform)}`)
92
92
  .addLine(`Platform version: ${logger.style.file(platformVersion)}`)
93
93
  .addLine(`Platform architecture: ${logger.style.file(getArchSync())}`)
94
+ .addLine(`CGroup version: ${logger.style.file(ctx.cgroupVersion)}`)
94
95
  .addEmptyLine()
95
96
  .addSeparator('Docker containers status')
96
97
 
@@ -77,6 +77,41 @@ const updateTableValues = async (
77
77
  )
78
78
  }
79
79
  }
80
+ /**
81
+ * Delete table in **magento** database
82
+ * @param {String} table Table name
83
+ * @param {{ path: string, value: string | number | null }[]} values
84
+ * @param {{ databaseConnection: import('../../typings/context').ListrContext['databaseConnection'], task: { skip(): void } }} param2
85
+ */
86
+ const deleteTableValues = async (
87
+ table,
88
+ values,
89
+ { databaseConnection, task }
90
+ ) => {
91
+ const [rows] = await databaseConnection.query(`
92
+ SELECT * FROM ${table}
93
+ WHERE ${values.map((p) => `path = '${p.path}'`).join(' OR ')};
94
+ `)
95
+
96
+ if (rows.filter(Boolean).length === 0) {
97
+ task.skip()
98
+ return
99
+ }
100
+
101
+ const configsToDelete = rows.filter(({ path }) =>
102
+ values.some((p) => p.path === path)
103
+ )
104
+
105
+ for (const config of configsToDelete) {
106
+ await databaseConnection.query(
107
+ `
108
+ DELETE FROM ${table}
109
+ WHERE config_id = ?;
110
+ `,
111
+ [config.config_id]
112
+ )
113
+ }
114
+ }
80
115
 
81
116
  /**
82
117
  * Insert values into table in **magento** database
@@ -213,6 +248,7 @@ const databaseQuery = async (
213
248
  module.exports = {
214
249
  updateTableValues,
215
250
  insertTableValues,
251
+ deleteTableValues,
216
252
  isTableExists,
217
253
  databaseQuery
218
254
  }
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.0-alpha.2",
6
+ "version": "2.4.0-alpha.3",
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": "ec855efd799e10f263e82b3bf610d400892db6dc"
62
+ "gitHead": "13e109c2d3475127df939b7a95bc0140927d4227"
63
63
  }