@scandipwa/magento-scripts 2.4.0-alpha.1 → 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.
- package/lib/commands/import-db.js +36 -3
- package/lib/config/docker.js +164 -56
- package/lib/config/port-config.js +46 -10
- package/lib/config/services/elasticsearch/default-es-env.js +1 -1
- package/lib/config/services/mariadb/versions/mariadb-10.2.js +3 -1
- package/lib/config/services/mariadb/versions/mariadb-10.3.js +3 -1
- package/lib/config/services/mariadb/versions/mariadb-10.4.js +3 -1
- package/lib/config/services/mariadb/versions/mariadb-10.6.js +3 -1
- package/lib/config/services/mariadb/versions/mariadb-11.4.js +3 -1
- package/lib/config/services/mariadb/versions/mariadb-11.6.js +3 -1
- package/lib/config/services/opensearch/default-os-env.js +1 -1
- package/lib/config/services/php/extensions/xdebug.js +1 -0
- package/lib/config/templates/nginx.template.conf +2 -2
- package/lib/config/templates/php-fpm.template.conf +3 -4
- package/lib/config/templates/ssl-terminator.template.conf +1 -1
- package/lib/tasks/database/create-magento-database.js +2 -1
- package/lib/tasks/database/fix-db.js +2 -0
- package/lib/tasks/database/import-dump-to-database.js +7 -4
- package/lib/tasks/database/import-remote-db/ssh/index.js +1 -1
- package/lib/tasks/database/import-remote-db/ssh/readymage.js +1 -1
- package/lib/tasks/database/import-remote-db/ssh/regular-server.js +1 -1
- package/lib/tasks/docker/containers/container-api.d.ts +5 -0
- package/lib/tasks/docker/containers/container-api.js +3 -1
- package/lib/tasks/docker/containers/tasks.js +86 -21
- package/lib/tasks/docker/project-image-builder.js +57 -44
- package/lib/tasks/docker/system/system-api.d.ts +66 -0
- package/lib/tasks/docker/system/system-api.js +28 -1
- package/lib/tasks/execute.js +1 -1
- package/lib/tasks/file-system/create-nginx-config.js +22 -8
- package/lib/tasks/file-system/create-php-fpm-config.js +6 -1
- package/lib/tasks/file-system/create-php-fpm-debug-config.js +6 -1
- package/lib/tasks/file-system/create-ssl-terminator-config.js +20 -7
- package/lib/tasks/magento/install-magento-project.js +101 -34
- package/lib/tasks/magento/setup-magento/check-file-permissions.php +55 -0
- package/lib/tasks/magento/setup-magento/disable-custom-admin-path.js +21 -0
- package/lib/tasks/magento/setup-magento/disable-maintenance-mode.js +13 -0
- package/lib/tasks/magento/setup-magento/index.js +2 -0
- package/lib/tasks/magento/setup-magento/make-magento-binaries-executable.js +44 -0
- package/lib/tasks/magento/setup-magento/set-deployment-mode.js +8 -5
- package/lib/tasks/magento/setup-magento/set-mail-config.js +16 -2
- package/lib/tasks/magento/setup-magento/setup-file-permissions.js +236 -0
- package/lib/tasks/php/php-container.js +21 -6
- package/lib/tasks/php/update-env-php.js +3 -9
- package/lib/tasks/requirements/cgroup-version.js +69 -0
- package/lib/tasks/requirements/elasticsearch-version.js +19 -3
- package/lib/tasks/requirements/index.js +3 -0
- package/lib/tasks/requirements/opensearch-version.js +1 -1
- package/lib/tasks/requirements/searchengine-version.js +1 -2
- package/lib/tasks/status/index.js +1 -0
- package/lib/util/database.js +36 -0
- package/lib/util/dockerfile-builder/build-instructions.js +5 -1
- package/lib/util/dockerfile-builder/types.d.ts +1 -1
- package/lib/util/execute-in-container.js +3 -1
- package/lib/util/get-installed-magento-version.js +60 -2
- package/lib/util/portscanner.js +3 -3
- package/lib/util/run-composer.js +1 -1
- package/lib/util/run-magento.js +2 -1
- package/lib/util/run-php.js +2 -1
- package/lib/util/set-config.js +4 -2
- package/package.json +16 -16
- package/typings/context.d.ts +4 -2
- package/typings/index.d.ts +10 -0
|
@@ -9,6 +9,11 @@ const getJsonFileData = require('../../util/get-jsonfile-data')
|
|
|
9
9
|
const KnownError = require('../../errors/known-error')
|
|
10
10
|
const UnknownError = require('../../errors/unknown-error')
|
|
11
11
|
const { runPHPContainerCommand } = require('../php/php-container')
|
|
12
|
+
const {
|
|
13
|
+
setupMagentoFilePermissions,
|
|
14
|
+
setupComposerCachePermissions
|
|
15
|
+
} = require('./setup-magento/setup-file-permissions')
|
|
16
|
+
const makeBinariesExecutable = require('./setup-magento/make-magento-binaries-executable')
|
|
12
17
|
|
|
13
18
|
const magentoProductEnterpriseEdition = 'magento/product-enterprise-edition'
|
|
14
19
|
const magentoProductCommunityEdition = 'magento/product-community-edition'
|
|
@@ -59,7 +64,8 @@ const adjustComposerJson = async (
|
|
|
59
64
|
? undefined
|
|
60
65
|
: (t) => {
|
|
61
66
|
task.output = t
|
|
62
|
-
}
|
|
67
|
+
},
|
|
68
|
+
useAutomaticUser: true
|
|
63
69
|
}
|
|
64
70
|
)
|
|
65
71
|
}
|
|
@@ -154,6 +160,46 @@ mv ${tempDir}/composer.json ${
|
|
|
154
160
|
)
|
|
155
161
|
}
|
|
156
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
|
+
|
|
157
203
|
/**
|
|
158
204
|
* @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
|
|
159
205
|
*/
|
|
@@ -191,7 +237,11 @@ const installMagentoProject = () => ({
|
|
|
191
237
|
'composer.lock': true
|
|
192
238
|
})
|
|
193
239
|
|
|
194
|
-
|
|
240
|
+
const isVendorFolderCorrupted = await getIsVendorFolderCorrupted(
|
|
241
|
+
baseConfig.magentoDir
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
if (isFsMatching && !isVendorFolderCorrupted) {
|
|
195
245
|
ctx.magentoFirstInstall = false
|
|
196
246
|
task.skip()
|
|
197
247
|
return
|
|
@@ -200,43 +250,60 @@ const installMagentoProject = () => ({
|
|
|
200
250
|
task.title = `Installing Magento ${magentoPackageVersion}`
|
|
201
251
|
task.output = `Creating Magento ${magentoPackageVersion} project`
|
|
202
252
|
|
|
203
|
-
if (!
|
|
204
|
-
await createMagentoProject(ctx, task, {
|
|
205
|
-
magentoProject,
|
|
206
|
-
magentoPackageVersion
|
|
207
|
-
})
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
if (!(await pathExists(path.join(process.cwd(), 'app', 'etc')))) {
|
|
211
|
-
await fs.promises.mkdir(path.join(process.cwd(), 'app', 'etc'), {
|
|
212
|
-
recursive: true
|
|
213
|
-
})
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
try {
|
|
217
|
-
await runComposerCommand(ctx, 'install', {
|
|
218
|
-
callback: !ctx.verbose
|
|
219
|
-
? undefined
|
|
220
|
-
: (t) => {
|
|
221
|
-
task.output = t
|
|
222
|
-
}
|
|
223
|
-
})
|
|
224
|
-
} catch (e) {
|
|
253
|
+
if (!isFsMatching) {
|
|
225
254
|
if (
|
|
226
|
-
|
|
227
|
-
e.message.includes('man-in-the-middle attack')
|
|
255
|
+
!(await pathExists(path.join(process.cwd(), 'composer.json')))
|
|
228
256
|
) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
257
|
+
await createMagentoProject(ctx, task, {
|
|
258
|
+
magentoProject,
|
|
259
|
+
magentoPackageVersion
|
|
260
|
+
})
|
|
233
261
|
}
|
|
234
262
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
+
}
|
|
238
271
|
}
|
|
239
|
-
|
|
272
|
+
|
|
273
|
+
return task.newListr([
|
|
274
|
+
setupMagentoFilePermissions(),
|
|
275
|
+
setupComposerCachePermissions(),
|
|
276
|
+
{
|
|
277
|
+
title: 'Installing Magento dependencies',
|
|
278
|
+
task: async () => {
|
|
279
|
+
try {
|
|
280
|
+
await runComposerCommand(ctx, 'install -vvv', {
|
|
281
|
+
callback: !ctx.verbose
|
|
282
|
+
? undefined
|
|
283
|
+
: (t) => {
|
|
284
|
+
task.output = t
|
|
285
|
+
}
|
|
286
|
+
})
|
|
287
|
+
} catch (e) {
|
|
288
|
+
if (
|
|
289
|
+
e instanceof UnknownError &&
|
|
290
|
+
e.message.includes('man-in-the-middle attack')
|
|
291
|
+
) {
|
|
292
|
+
throw new KnownError(`Probably you haven't setup pubkeys in composer.
|
|
293
|
+
Please run ${logger.style.command(
|
|
294
|
+
'composer diagnose'
|
|
295
|
+
)} in cli to get mode.\n\n${e}`)
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
throw new UnknownError(
|
|
299
|
+
`Unexpected error during composer install.\n\n${e}`
|
|
300
|
+
)
|
|
301
|
+
}
|
|
302
|
+
ctx.magentoFirstInstall = true
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
makeBinariesExecutable()
|
|
306
|
+
])
|
|
240
307
|
},
|
|
241
308
|
options: {
|
|
242
309
|
bottomBar: 10
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
// receive directories from command line arguments
|
|
3
|
+
$directories = explode(",", $argv[1]);
|
|
4
|
+
|
|
5
|
+
$results = [];
|
|
6
|
+
|
|
7
|
+
foreach ($directories as $directory) {
|
|
8
|
+
$result = [];
|
|
9
|
+
$result['directory'] = $directory;
|
|
10
|
+
$result['exists'] = file_exists($directory);
|
|
11
|
+
$result['writable'] = is_writable($directory);
|
|
12
|
+
$result['readable'] = is_readable($directory);
|
|
13
|
+
|
|
14
|
+
if ($result['exists']) {
|
|
15
|
+
$result['owner'] = posix_getpwuid(fileowner($directory));
|
|
16
|
+
$result['group'] = posix_getgrgid(filegroup($directory));
|
|
17
|
+
$result['current_user'] = posix_getpwuid(posix_geteuid());
|
|
18
|
+
|
|
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);
|
|
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
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$results[] = $result;
|
|
53
|
+
}
|
|
54
|
+
|
|
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
|
})
|
|
@@ -13,6 +13,7 @@ const urnHighlighter = require('./urn-highlighter')
|
|
|
13
13
|
const adjustFullPageCache = require('./adjust-full-page-cache')
|
|
14
14
|
const updateEnvPHP = require('../../php/update-env-php')
|
|
15
15
|
const setMailConfig = require('./set-mail-config')
|
|
16
|
+
const { setupMagentoFilePermissions } = require('./setup-file-permissions')
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* @param {Object} [options]
|
|
@@ -34,6 +35,7 @@ const setupMagento = (options = {}) => ({
|
|
|
34
35
|
return task.newListr(
|
|
35
36
|
[
|
|
36
37
|
waitingForRedis(),
|
|
38
|
+
setupMagentoFilePermissions(),
|
|
37
39
|
updateEnvPHP(),
|
|
38
40
|
migrateDatabase(),
|
|
39
41
|
flushRedisConfig(),
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Yes, I know binary files are files in binary format, but this is a common term for executable files
|
|
2
|
+
|
|
3
|
+
const fs = require('fs')
|
|
4
|
+
const path = require('path')
|
|
5
|
+
const pathExists = require('../../../util/path-exists')
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
9
|
+
*/
|
|
10
|
+
const makeBinariesExecutable = () => ({
|
|
11
|
+
task: async (ctx, task) => {
|
|
12
|
+
const directoriesWithBinFiles = [
|
|
13
|
+
path.join(ctx.config.baseConfig.magentoDir, 'bin'),
|
|
14
|
+
path.join(ctx.config.baseConfig.magentoDir, 'vendor', 'bin')
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
for (const directory of directoriesWithBinFiles) {
|
|
18
|
+
if (!(await pathExists(directory))) {
|
|
19
|
+
continue
|
|
20
|
+
}
|
|
21
|
+
const files = await fs.promises.readdir(directory, {
|
|
22
|
+
withFileTypes: true
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
for (const file of files) {
|
|
26
|
+
if (!file.isFile()) {
|
|
27
|
+
continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const filePath = path.join(directory, file.name)
|
|
31
|
+
const stats = await fs.promises.stat(filePath)
|
|
32
|
+
|
|
33
|
+
if (!filePath.startsWith(file.name) && stats.mode & 0o111) {
|
|
34
|
+
continue
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
task.output = `Making ${filePath} executable`
|
|
38
|
+
await fs.promises.chmod(filePath, 0o755)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
module.exports = makeBinariesExecutable
|
|
@@ -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
|
|
16
|
-
|
|
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 (
|
|
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/
|
|
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
|
-
: '
|
|
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 }
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
const os = require('os')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const {
|
|
5
|
+
runPHPContainerCommandTask,
|
|
6
|
+
runPHPContainerCommand
|
|
7
|
+
} = require('../../php/php-container')
|
|
8
|
+
|
|
9
|
+
const directoriesToCheck = [
|
|
10
|
+
'var',
|
|
11
|
+
'generated',
|
|
12
|
+
'vendor',
|
|
13
|
+
'pub/media',
|
|
14
|
+
'pub/static',
|
|
15
|
+
'app/etc'
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string[]} directories
|
|
20
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
21
|
+
*/
|
|
22
|
+
const makeNewFilesCreatedInFolderUseDirectoryGroup = (directories) => ({
|
|
23
|
+
title: 'Make new files created in folder use directory group',
|
|
24
|
+
task: async (ctx, task) => {
|
|
25
|
+
if (directories.length === 0) {
|
|
26
|
+
task.skip()
|
|
27
|
+
return
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return task.newListr([
|
|
31
|
+
runPHPContainerCommandTask(
|
|
32
|
+
`find ${directories.join(' ')} -type d -exec chmod g+ws {} +`,
|
|
33
|
+
{
|
|
34
|
+
// should prevent command from failing the task
|
|
35
|
+
// if the folder does not exist
|
|
36
|
+
withCode: true,
|
|
37
|
+
useAutomaticUser: false
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
])
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* @param {string[]} directories
|
|
46
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
47
|
+
*/
|
|
48
|
+
const makeFilesWritableForGroupMembers = (directories) => ({
|
|
49
|
+
title: 'Make files writable for group members',
|
|
50
|
+
task: async (ctx, task) => {
|
|
51
|
+
if (directories.length === 0) {
|
|
52
|
+
task.skip()
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return task.newListr([
|
|
57
|
+
runPHPContainerCommandTask(
|
|
58
|
+
`find ${directories.join(
|
|
59
|
+
' '
|
|
60
|
+
)} -type -type f -exec chmod g+w {} +`,
|
|
61
|
+
{
|
|
62
|
+
// should prevent command from failing the task
|
|
63
|
+
// if the folder does not exist
|
|
64
|
+
withCode: true
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
])
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {string} folder
|
|
73
|
+
* @param {string | number} user
|
|
74
|
+
* @param {string | number} [group]
|
|
75
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
76
|
+
*/
|
|
77
|
+
const makeFolderOwnedByUser = (folder, user, group) => ({
|
|
78
|
+
title: `Make folder ${folder} owned by ${user} user${
|
|
79
|
+
group ? ` and ${group} group` : ''
|
|
80
|
+
}`,
|
|
81
|
+
task: (ctx, task) =>
|
|
82
|
+
task.newListr([
|
|
83
|
+
runPHPContainerCommandTask(
|
|
84
|
+
`chown -R ${user}${group ? `:${group}` : ''} ${folder}`,
|
|
85
|
+
{
|
|
86
|
+
user: 'root:root'
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
])
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
/**
|
|
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}[]>}
|
|
96
|
+
*/
|
|
97
|
+
const checkDirectoriesPermissions = async (ctx, directories) => {
|
|
98
|
+
const checkPHPPermissionsFileName = 'check-file-permissions.php'
|
|
99
|
+
const cacheDirFilePath = path.join(
|
|
100
|
+
ctx.config.baseConfig.cacheDir,
|
|
101
|
+
`${Date.now()}-${checkPHPPermissionsFileName}`
|
|
102
|
+
)
|
|
103
|
+
await fs.promises.copyFile(
|
|
104
|
+
path.join(__dirname, checkPHPPermissionsFileName),
|
|
105
|
+
cacheDirFilePath
|
|
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}`
|
|
113
|
+
|
|
114
|
+
const result = await runPHPContainerCommand(
|
|
115
|
+
ctx,
|
|
116
|
+
`php ${path.relative(
|
|
117
|
+
ctx.config.baseConfig.containerMagentoDir,
|
|
118
|
+
cacheDirFilePath
|
|
119
|
+
)} ${directories.join(',')}`,
|
|
120
|
+
{
|
|
121
|
+
user: userWithGroup,
|
|
122
|
+
cwd: ctx.config.baseConfig.containerMagentoDir
|
|
123
|
+
}
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
await fs.promises.unlink(cacheDirFilePath)
|
|
127
|
+
|
|
128
|
+
return JSON.parse(result)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
133
|
+
*/
|
|
134
|
+
const setupMagentoFilePermissions = () => ({
|
|
135
|
+
title: 'Setting Magento file permissions',
|
|
136
|
+
task: async (ctx, task) => {
|
|
137
|
+
const parsedResult = await checkDirectoriesPermissions(
|
|
138
|
+
ctx,
|
|
139
|
+
directoriesToCheck.map((directory) =>
|
|
140
|
+
path.join(ctx.config.baseConfig.containerMagentoDir, directory)
|
|
141
|
+
)
|
|
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
|
+
}
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @returns {import('listr2').ListrTask<import('../../../../typings/context').ListrContext>}
|
|
203
|
+
*/
|
|
204
|
+
const setupComposerCachePermissions = () => ({
|
|
205
|
+
title: 'Setting Composer Cache permissions',
|
|
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
|
+
})
|
|
227
|
+
])
|
|
228
|
+
}
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
module.exports = {
|
|
232
|
+
makeFilesWritableForGroupMembers,
|
|
233
|
+
makeNewFilesCreatedInFolderUseDirectoryGroup,
|
|
234
|
+
setupMagentoFilePermissions,
|
|
235
|
+
setupComposerCachePermissions
|
|
236
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
/* eslint-disable no-use-before-define */
|
|
2
|
+
const os = require('os')
|
|
2
3
|
const { deepmerge } = require('../../util/deepmerge')
|
|
3
4
|
const { containerApi } = require('../docker/containers')
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
|
-
* @
|
|
7
|
+
* @param {Parameters<typeof import('./php-container')['runPHPContainerCommand']>[0]} ctx
|
|
8
|
+
* @param {Parameters<typeof import('./php-container')['runPHPContainerCommand']>[1]} command
|
|
9
|
+
* @param {Parameters<typeof import('./php-container')['runPHPContainerCommand']>[2] & { useAutomaticUser?: boolean}} [options]
|
|
7
10
|
*/
|
|
8
11
|
const runPHPContainerCommand = async (ctx, command, options = {}) => {
|
|
12
|
+
const { useAutomaticUser = true } = options
|
|
9
13
|
const { php } = ctx.config.docker.getContainers(ctx.ports)
|
|
10
14
|
|
|
11
15
|
const containers = await containerApi.ls({
|
|
@@ -23,11 +27,21 @@ const runPHPContainerCommand = async (ctx, command, options = {}) => {
|
|
|
23
27
|
php,
|
|
24
28
|
{
|
|
25
29
|
detach: false,
|
|
26
|
-
rm: true
|
|
27
|
-
},
|
|
28
|
-
{
|
|
30
|
+
rm: true,
|
|
29
31
|
command
|
|
30
|
-
}
|
|
32
|
+
},
|
|
33
|
+
useAutomaticUser && ctx.platform === 'linux'
|
|
34
|
+
? {
|
|
35
|
+
user: `${os.userInfo().username}:${
|
|
36
|
+
os.userInfo().username
|
|
37
|
+
}`
|
|
38
|
+
}
|
|
39
|
+
: {},
|
|
40
|
+
options.user
|
|
41
|
+
? {
|
|
42
|
+
user: options.user
|
|
43
|
+
}
|
|
44
|
+
: {}
|
|
31
45
|
),
|
|
32
46
|
options
|
|
33
47
|
)
|
|
@@ -68,7 +82,8 @@ const execPHPContainerCommand = async (ctx, command, options = {}) => {
|
|
|
68
82
|
{
|
|
69
83
|
container: php.name,
|
|
70
84
|
...deepmerge(php, options.env ? { env: options.env } : {}),
|
|
71
|
-
command
|
|
85
|
+
command,
|
|
86
|
+
...(options.user ? { user: options.user } : {})
|
|
72
87
|
},
|
|
73
88
|
options
|
|
74
89
|
)
|