@scandipwa/magento-scripts 2.4.12-alpha.0 → 2.4.13
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/.oxlintrc.json +32 -0
- package/exec.js +1 -1
- package/index.js +1 -4
- package/lib/commands/status.js +45 -7
- package/lib/config/docker.js +1 -1
- package/lib/config/system-config.js +2 -2
- package/lib/tasks/database/import-remote-db/ssh/readymage.js +2 -1
- package/lib/tasks/docker/containers/tasks.js +1 -1
- package/lib/tasks/file-system/create-git-hook-notification.js +1 -1
- package/lib/tasks/file-system/create-nginx-config.js +4 -1
- package/lib/tasks/file-system/create-phpstorm-config/php-docker-settings-config/index.js +6 -10
- package/lib/tasks/magento/install-magento-project.js +1 -1
- package/lib/tasks/magento/setup-magento/set-base-url.js +7 -19
- package/lib/tasks/magento/setup-magento/waiting-for-varnish.js +2 -2
- package/lib/tasks/requirements/docker/running-status.js +1 -1
- package/lib/tasks/requirements/searchengine-version.js +1 -1
- package/lib/tasks/status/index.js +186 -5
- package/lib/tasks/theme/build-theme.js +1 -1
- package/lib/util/analytics.js +2 -2
- package/lib/util/exec-async-command.js +1 -1
- package/lib/util/ip.js +1 -1
- package/lib/util/is-wsl.js +1 -1
- package/lib/util/path-exists-sync.js +1 -1
- package/lib/util/path-exists.js +1 -1
- package/lib/util/store-domains.js +71 -0
- package/lib/util/store-domains.test.js +52 -0
- package/lib/util/systemctl.js +3 -3
- package/package.json +6 -2
- package/.eslintrc +0 -10
- package/lib/util/ensure-agents-md.js +0 -79
package/.oxlintrc.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "../../node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"env": {
|
|
4
|
+
"node": true,
|
|
5
|
+
"es2022": true
|
|
6
|
+
},
|
|
7
|
+
"globals": {
|
|
8
|
+
"verbose": "readonly",
|
|
9
|
+
"NodeJS": "readonly",
|
|
10
|
+
"BufferEncoding": "readonly"
|
|
11
|
+
},
|
|
12
|
+
"categories": {
|
|
13
|
+
"correctness": "error"
|
|
14
|
+
},
|
|
15
|
+
"ignorePatterns": [
|
|
16
|
+
"**/node_modules/**",
|
|
17
|
+
"**/lib/config/templates/**",
|
|
18
|
+
"**/typings/**",
|
|
19
|
+
"**/*.d.ts"
|
|
20
|
+
],
|
|
21
|
+
"rules": {
|
|
22
|
+
"no-unused-vars": [
|
|
23
|
+
"warn",
|
|
24
|
+
{
|
|
25
|
+
"argsIgnorePattern": "^_",
|
|
26
|
+
"caughtErrorsIgnorePattern": "^_",
|
|
27
|
+
"varsIgnorePattern": "^_"
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"unicorn/no-useless-fallback-in-spread": "off"
|
|
31
|
+
}
|
|
32
|
+
}
|
package/exec.js
CHANGED
|
@@ -93,7 +93,7 @@ const newVersionIsAPatch = (latestVersion, currentVersion) => {
|
|
|
93
93
|
process.isOutOfDateVersion = true
|
|
94
94
|
process.isOutOfDateVersionMessage = message
|
|
95
95
|
}
|
|
96
|
-
} catch
|
|
96
|
+
} catch {
|
|
97
97
|
logger.warn(`Package ${logger.style.misc(name)} is not yet published.`)
|
|
98
98
|
logger.log() // add empty line
|
|
99
99
|
}
|
package/index.js
CHANGED
|
@@ -6,7 +6,6 @@ const logger = require('@scandipwa/scandipwa-dev-utils/logger')
|
|
|
6
6
|
const semver = require('semver')
|
|
7
7
|
const isInstalledGlobally = require('is-installed-globally')
|
|
8
8
|
const isRunningRoot = require('./lib/util/is-running-root')
|
|
9
|
-
const ensureAgentsMd = require('./lib/util/ensure-agents-md')
|
|
10
9
|
|
|
11
10
|
if (isRunningRoot()) {
|
|
12
11
|
logger.error('Root privileges detected!')
|
|
@@ -28,8 +27,6 @@ If you are experiencing problems with ${logger.style.misc(
|
|
|
28
27
|
process.exit(1)
|
|
29
28
|
}
|
|
30
29
|
|
|
31
|
-
ensureAgentsMd()
|
|
32
|
-
|
|
33
30
|
const commands = [
|
|
34
31
|
require('./lib/commands/link'),
|
|
35
32
|
require('./lib/commands/logs'),
|
|
@@ -118,7 +115,7 @@ const newVersionIsAPatch = (latestVersion, currentVersion) => {
|
|
|
118
115
|
process.isOutOfDateVersion = true
|
|
119
116
|
process.isOutOfDateVersionMessage = message
|
|
120
117
|
}
|
|
121
|
-
} catch
|
|
118
|
+
} catch {
|
|
122
119
|
logger.warn(`Package ${logger.style.misc(name)} is not yet published.`)
|
|
123
120
|
logger.log() // add empty line
|
|
124
121
|
}
|
package/lib/commands/status.js
CHANGED
|
@@ -3,7 +3,7 @@ const { Listr } = require('listr2')
|
|
|
3
3
|
const getMagentoVersionConfig = require('../config/get-magento-version-config')
|
|
4
4
|
const { getCachedPorts } = require('../config/get-port-config')
|
|
5
5
|
|
|
6
|
-
const { prettyStatus } = require('../tasks/status')
|
|
6
|
+
const { prettyStatus, simpleStatus } = require('../tasks/status')
|
|
7
7
|
const { checkRequirements } = require('../tasks/requirements')
|
|
8
8
|
const { statusContainers } = require('../tasks/docker/containers')
|
|
9
9
|
const getProjectConfiguration = require('../config/get-project-configuration')
|
|
@@ -20,10 +20,35 @@ module.exports = (yargs) => {
|
|
|
20
20
|
yargs.command(
|
|
21
21
|
'status',
|
|
22
22
|
'Show application status',
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
(yargs) => {
|
|
24
|
+
yargs
|
|
25
|
+
.option('non-interactive', {
|
|
26
|
+
alias: 'n',
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
default: false,
|
|
29
|
+
description:
|
|
30
|
+
'Print a plain-text status summary (for AI terminals and scripts)'
|
|
31
|
+
})
|
|
32
|
+
.option('verbose', {
|
|
33
|
+
alias: 'v',
|
|
34
|
+
type: 'boolean',
|
|
35
|
+
default: false,
|
|
36
|
+
description:
|
|
37
|
+
'Retrieve Docker image and volume sizes (slower, off by default)'
|
|
38
|
+
})
|
|
39
|
+
},
|
|
25
40
|
async (args) => {
|
|
26
41
|
const silent = /** @type {boolean} */ (args.silent)
|
|
42
|
+
// A non-TTY stdout (pipe, CI, AI terminal) is inherently
|
|
43
|
+
// non-interactive; the -n flag forces it even inside a TTY.
|
|
44
|
+
const nonInteractive =
|
|
45
|
+
!!(args.nonInteractive || args.n) || !process.stdout.isTTY
|
|
46
|
+
// Enumerating per-image/-volume sizes shells out to
|
|
47
|
+
// `docker system df --verbose`, which is slow; skip it by default.
|
|
48
|
+
// The non-interactive report already prints the full status; the
|
|
49
|
+
// --verbose flag only adds the Docker image/volume sizes (and is
|
|
50
|
+
// what the pretty renderer needs to render them at all).
|
|
51
|
+
const verbose = !!(args.verbose || args.v)
|
|
27
52
|
const tasks = new Listr(
|
|
28
53
|
[
|
|
29
54
|
checkRequirements(),
|
|
@@ -40,6 +65,9 @@ module.exports = (yargs) => {
|
|
|
40
65
|
checkSearchEngineVersion(),
|
|
41
66
|
{
|
|
42
67
|
title: 'Retrieving Docker System data',
|
|
68
|
+
skip: () =>
|
|
69
|
+
!verbose &&
|
|
70
|
+
'Docker image and volume sizes omitted (pass --verbose to include them)',
|
|
43
71
|
task: async (ctx) => {
|
|
44
72
|
ctx.systemDFData =
|
|
45
73
|
await systemApi.df({
|
|
@@ -59,15 +87,25 @@ module.exports = (yargs) => {
|
|
|
59
87
|
{
|
|
60
88
|
concurrent: false,
|
|
61
89
|
exitOnError: false,
|
|
62
|
-
ctx: {
|
|
63
|
-
|
|
64
|
-
|
|
90
|
+
ctx: {
|
|
91
|
+
throwMagentoVersionMissing: true,
|
|
92
|
+
...args,
|
|
93
|
+
silent,
|
|
94
|
+
nonInteractive,
|
|
95
|
+
verbose
|
|
96
|
+
},
|
|
97
|
+
renderer: silent || nonInteractive ? 'silent' : 'default',
|
|
65
98
|
rendererOptions: { collapse: false, clearOutput: false }
|
|
66
99
|
}
|
|
67
100
|
)
|
|
68
101
|
|
|
69
102
|
try {
|
|
70
|
-
|
|
103
|
+
const ctx = await tasks.run()
|
|
104
|
+
if (nonInteractive) {
|
|
105
|
+
simpleStatus(ctx)
|
|
106
|
+
} else {
|
|
107
|
+
await prettyStatus(ctx)
|
|
108
|
+
}
|
|
71
109
|
} catch (e) {
|
|
72
110
|
logger.error(e.message || e)
|
|
73
111
|
process.exit(1)
|
package/lib/config/docker.js
CHANGED
|
@@ -23,7 +23,7 @@ const rosettaTranslatedContainers =
|
|
|
23
23
|
*/
|
|
24
24
|
const volumeDirectives = (directives) => {
|
|
25
25
|
const directivesResult = Object.entries(directives)
|
|
26
|
-
.filter(([
|
|
26
|
+
.filter(([, value]) => value === true)
|
|
27
27
|
.map(([name]) => name)
|
|
28
28
|
.join(',')
|
|
29
29
|
|
|
@@ -25,7 +25,7 @@ const getSystemConfig = async ({ validate = true } = {}) => {
|
|
|
25
25
|
let userSystemConfigParsed
|
|
26
26
|
try {
|
|
27
27
|
userSystemConfigParsed = JSON.parse(userSystemConfig)
|
|
28
|
-
} catch
|
|
28
|
+
} catch {
|
|
29
29
|
throw new KnownError(
|
|
30
30
|
`System configuration file is not a valid JSON!\n\nFile location: ${systemConfigPath}`
|
|
31
31
|
)
|
|
@@ -61,7 +61,7 @@ const getSystemConfigSync = ({ validate = true } = {}) => {
|
|
|
61
61
|
let userSystemConfigParsed
|
|
62
62
|
try {
|
|
63
63
|
userSystemConfigParsed = JSON.parse(userSystemConfig)
|
|
64
|
-
} catch
|
|
64
|
+
} catch {
|
|
65
65
|
throw new KnownError(
|
|
66
66
|
`System configuration file is not a valid JSON!\n\nFile location: ${systemConfigPath}`
|
|
67
67
|
)
|
|
@@ -172,7 +172,7 @@ const startContainers = () => ({
|
|
|
172
172
|
|
|
173
173
|
const missingContainers = Object.entries(containers)
|
|
174
174
|
.filter(
|
|
175
|
-
([
|
|
175
|
+
([, { name }]) =>
|
|
176
176
|
!containerList.some((c) => c.Names === name)
|
|
177
177
|
)
|
|
178
178
|
.map(([nameWithoutPrefix, containerOptions]) => ({
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const path = require('path')
|
|
2
2
|
const setConfigFile = require('../../util/set-config')
|
|
3
3
|
const UnknownError = require('../../errors/unknown-error')
|
|
4
|
+
const {
|
|
5
|
+
shouldUseStoreDomainMapping
|
|
6
|
+
} = require('../../util/store-domains')
|
|
4
7
|
|
|
5
8
|
/**
|
|
6
9
|
* @returns {import('listr2').ListrTask<import('../../../typings/context').ListrContext>}
|
|
@@ -35,7 +38,7 @@ const createNginxConfig = () => ({
|
|
|
35
38
|
}
|
|
36
39
|
|
|
37
40
|
const useStoreDomainMapping =
|
|
38
|
-
|
|
41
|
+
shouldUseStoreDomainMapping(storeDomains)
|
|
39
42
|
|
|
40
43
|
try {
|
|
41
44
|
await setConfigFile({
|
|
@@ -18,14 +18,12 @@ const setupPhpDockerSettingsConfig = () => ({
|
|
|
18
18
|
)
|
|
19
19
|
const phpDockerSettingsConfigs =
|
|
20
20
|
phpDockerSettingsConfigContent.project.component
|
|
21
|
-
const hasChanges = await
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
)
|
|
26
|
-
])
|
|
21
|
+
const hasChanges = await setupPHPDockerContainerSettingsConfig(
|
|
22
|
+
phpDockerSettingsConfigs,
|
|
23
|
+
ctx
|
|
24
|
+
)
|
|
27
25
|
|
|
28
|
-
if (hasChanges
|
|
26
|
+
if (hasChanges === true) {
|
|
29
27
|
await buildXmlFile(
|
|
30
28
|
phpDockerSettingsConfig.path,
|
|
31
29
|
phpDockerSettingsConfigContent
|
|
@@ -41,9 +39,7 @@ const setupPhpDockerSettingsConfig = () => ({
|
|
|
41
39
|
const phpDockerSettingsConfigs =
|
|
42
40
|
phpDockerSettingsConfigContent.project.component
|
|
43
41
|
|
|
44
|
-
await
|
|
45
|
-
setupPHPDockerContainerSettingsConfig(phpDockerSettingsConfigs, ctx)
|
|
46
|
-
])
|
|
42
|
+
await setupPHPDockerContainerSettingsConfig(phpDockerSettingsConfigs, ctx)
|
|
47
43
|
|
|
48
44
|
await buildXmlFile(
|
|
49
45
|
phpDockerSettingsConfig.path,
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
const { updateTableValues, databaseQuery } = require('../../../util/database')
|
|
2
2
|
const KnownError = require('../../../errors/known-error')
|
|
3
|
+
const {
|
|
4
|
+
resolveStoreDomainsForScopes
|
|
5
|
+
} = require('../../../util/store-domains')
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* @param {number} scopeId
|
|
@@ -129,25 +132,10 @@ const setBaseUrl = () => ({
|
|
|
129
132
|
)
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
const storeDomainsWithMapping =
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
(entity) => entity.code === key
|
|
137
|
-
)
|
|
138
|
-
if (entity) {
|
|
139
|
-
return {
|
|
140
|
-
...acc,
|
|
141
|
-
[entity.code]: {
|
|
142
|
-
scopeId: entity[idField],
|
|
143
|
-
domain: val
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return acc
|
|
149
|
-
},
|
|
150
|
-
{}
|
|
135
|
+
const storeDomainsWithMapping = resolveStoreDomainsForScopes(
|
|
136
|
+
storeDomains,
|
|
137
|
+
entities,
|
|
138
|
+
idField
|
|
151
139
|
)
|
|
152
140
|
|
|
153
141
|
// Check for missing store codes when runType is 'store'
|
|
@@ -80,7 +80,7 @@ const waitingForVarnish = () => ({
|
|
|
80
80
|
} else {
|
|
81
81
|
break
|
|
82
82
|
}
|
|
83
|
-
} catch
|
|
83
|
+
} catch {
|
|
84
84
|
tries++
|
|
85
85
|
await sleep(200)
|
|
86
86
|
}
|
|
@@ -155,7 +155,7 @@ Do you want to try resolving this issue by replacing ${logger.style.file(
|
|
|
155
155
|
connectionFixed = true
|
|
156
156
|
break
|
|
157
157
|
}
|
|
158
|
-
} catch
|
|
158
|
+
} catch {
|
|
159
159
|
await sleep(200)
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -8,6 +8,16 @@ const { getArchSync } = require('../../util/arch')
|
|
|
8
8
|
const ConsoleBlock = require('../../util/console-block')
|
|
9
9
|
const { getInstanceMetadata } = require('../../util/instance-metadata')
|
|
10
10
|
|
|
11
|
+
// eslint-disable-next-line no-control-regex
|
|
12
|
+
const consoleStyleReplacer = /[]\[\S+?m/g
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* chalk already drops styling when stdout is not a TTY, but strip defensively
|
|
16
|
+
* so the plain output stays clean even under FORCE_COLOR.
|
|
17
|
+
* @param {string} str
|
|
18
|
+
*/
|
|
19
|
+
const stripStyle = (str) => String(str).replace(consoleStyleReplacer, '')
|
|
20
|
+
|
|
11
21
|
/**
|
|
12
22
|
* @param {any} str
|
|
13
23
|
* @returns {str is string}
|
|
@@ -18,7 +28,7 @@ const isJSON = (str) => {
|
|
|
18
28
|
if (typeof result === 'object') {
|
|
19
29
|
return true
|
|
20
30
|
}
|
|
21
|
-
} catch
|
|
31
|
+
} catch {
|
|
22
32
|
//
|
|
23
33
|
}
|
|
24
34
|
|
|
@@ -230,9 +240,10 @@ const prettyStatus = async (ctx) => {
|
|
|
230
240
|
|
|
231
241
|
Object.values(volumes)
|
|
232
242
|
.map((volume) => {
|
|
233
|
-
volume.volumeData =
|
|
234
|
-
|
|
235
|
-
|
|
243
|
+
volume.volumeData =
|
|
244
|
+
systemDFData &&
|
|
245
|
+
systemDFData.Volumes &&
|
|
246
|
+
systemDFData.Volumes.find((v) => v.Name === volume.name)
|
|
236
247
|
|
|
237
248
|
return volume
|
|
238
249
|
})
|
|
@@ -273,4 +284,174 @@ const prettyStatus = async (ctx) => {
|
|
|
273
284
|
block.log()
|
|
274
285
|
}
|
|
275
286
|
|
|
276
|
-
|
|
287
|
+
/**
|
|
288
|
+
* Plain-text status summary for non-interactive environments (AI agents, CI,
|
|
289
|
+
* pipes). No box drawing or ANSI styling — just the facts an agent needs to
|
|
290
|
+
* act, plus the commands to run next.
|
|
291
|
+
* @param {import('../../../typings/context').ListrContext & { containers: ReturnType<Awaited<ReturnType<import('../../config/docker')>>['getContainers']> }} ctx
|
|
292
|
+
*/
|
|
293
|
+
const simpleStatus = (ctx) => {
|
|
294
|
+
const {
|
|
295
|
+
config: { baseConfig },
|
|
296
|
+
magentoVersion,
|
|
297
|
+
composerVersion,
|
|
298
|
+
dockerVersion,
|
|
299
|
+
containers,
|
|
300
|
+
systemDFData,
|
|
301
|
+
verbose
|
|
302
|
+
} = ctx
|
|
303
|
+
|
|
304
|
+
const lines = []
|
|
305
|
+
|
|
306
|
+
lines.push(
|
|
307
|
+
`magento-scripts ${packageVersion} — status (non-interactive)`,
|
|
308
|
+
'',
|
|
309
|
+
`Project: ${baseConfig.prefix}`,
|
|
310
|
+
`Location: ${process.cwd()}`,
|
|
311
|
+
`Magento: ${magentoVersion || 'unknown'}`,
|
|
312
|
+
`PHP: ${ctx.phpVersion || 'unknown'}`,
|
|
313
|
+
`Composer: ${composerVersion || 'unknown'}`,
|
|
314
|
+
`Docker: ${dockerVersion || 'unknown'}`,
|
|
315
|
+
`Platform: ${ctx.platform} (${getArchSync()})`
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
lines.push(
|
|
319
|
+
`Platform version: ${ctx.platformVersion || 'unknown'}`,
|
|
320
|
+
`CGroup version: ${ctx.cgroupVersion || 'unknown'}`
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
const projectCreatedAt = getProjectCreatedAt()
|
|
324
|
+
if (projectCreatedAt) {
|
|
325
|
+
lines.push(
|
|
326
|
+
`Project created: ${projectCreatedAt.toDateString()} ${projectCreatedAt.toTimeString()}`
|
|
327
|
+
)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
lines.push('', 'Containers:')
|
|
331
|
+
|
|
332
|
+
let anyRunning = false
|
|
333
|
+
|
|
334
|
+
Object.values(containers).forEach((container) => {
|
|
335
|
+
const state = container.status && container.status.State
|
|
336
|
+
let status
|
|
337
|
+
|
|
338
|
+
if (state && state.Health && state.Status === 'running') {
|
|
339
|
+
status = `running (${state.Health.Status})`
|
|
340
|
+
anyRunning = true
|
|
341
|
+
} else if (state && state.Status && state.Status !== 'exited') {
|
|
342
|
+
status = state.Status
|
|
343
|
+
if (state.Status === 'running') {
|
|
344
|
+
anyRunning = true
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
status = 'not running'
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
lines.push(` ${container._ || container.name}: ${status}`)
|
|
351
|
+
|
|
352
|
+
lines.push(` Name: ${container.name}`)
|
|
353
|
+
|
|
354
|
+
const image =
|
|
355
|
+
container.status &&
|
|
356
|
+
container.status.Config &&
|
|
357
|
+
container.status.Config.Image
|
|
358
|
+
? container.status.Config.Image
|
|
359
|
+
: container.image
|
|
360
|
+
lines.push(` Image: ${image}`, ` Network: ${container.network}`)
|
|
361
|
+
|
|
362
|
+
if (
|
|
363
|
+
status !== 'not running' &&
|
|
364
|
+
container.forwardedPorts &&
|
|
365
|
+
container.forwardedPorts.length > 0
|
|
366
|
+
) {
|
|
367
|
+
lines.push(' Port forwarding:')
|
|
368
|
+
container.forwardedPorts.forEach((port) => {
|
|
369
|
+
const { host, hostPort, containerPort } = parsePort(port)
|
|
370
|
+
if (container.network !== 'host') {
|
|
371
|
+
lines.push(
|
|
372
|
+
` ${host}:${hostPort} -> ${containerPort} (host -> container)`
|
|
373
|
+
)
|
|
374
|
+
} else {
|
|
375
|
+
lines.push(
|
|
376
|
+
` Running on host network - ${host}:${hostPort}`
|
|
377
|
+
)
|
|
378
|
+
}
|
|
379
|
+
})
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (container.env && Object.keys(container.env).length > 0) {
|
|
383
|
+
lines.push(' Environment variables:')
|
|
384
|
+
for (const [envName, envValue] of Object.entries(container.env)) {
|
|
385
|
+
lines.push(` ${envName}=${envValue}`)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (container.description) {
|
|
390
|
+
lines.push(' Description:')
|
|
391
|
+
container.description.split('\n').forEach((line) => {
|
|
392
|
+
lines.push(` ${stripStyle(line)}`)
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
lines.push('', 'Volumes:')
|
|
398
|
+
|
|
399
|
+
const { volumes } = ctx.config.docker
|
|
400
|
+
|
|
401
|
+
Object.values(volumes).forEach((volume) => {
|
|
402
|
+
const volumeData =
|
|
403
|
+
systemDFData &&
|
|
404
|
+
systemDFData.Volumes &&
|
|
405
|
+
systemDFData.Volumes.find((v) => v.Name === volume.name)
|
|
406
|
+
|
|
407
|
+
lines.push(` ${volume.name}`)
|
|
408
|
+
|
|
409
|
+
if (volumeData) {
|
|
410
|
+
lines.push(` Size: ${volumeData.Size}`)
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
if (ctx.isDockerDesktop && volume.opt && volume.opt.device) {
|
|
414
|
+
lines.push(
|
|
415
|
+
` Mountpoint: ${volume.opt.device.replace(
|
|
416
|
+
process.cwd(),
|
|
417
|
+
'<project location>'
|
|
418
|
+
)}`
|
|
419
|
+
)
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
if (!verbose) {
|
|
424
|
+
lines.push(' (volume sizes omitted — pass --verbose to include them)')
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const instanceMetadata = getInstanceMetadata(ctx)
|
|
428
|
+
|
|
429
|
+
lines.push('', 'Frontend:')
|
|
430
|
+
instanceMetadata.frontend.forEach(({ title, text }) => {
|
|
431
|
+
lines.push(` ${stripStyle(title)}: ${stripStyle(text)}`)
|
|
432
|
+
})
|
|
433
|
+
|
|
434
|
+
lines.push('', 'Admin:')
|
|
435
|
+
instanceMetadata.admin.forEach(({ title, text }) => {
|
|
436
|
+
lines.push(` ${stripStyle(title)}: ${stripStyle(text)}`)
|
|
437
|
+
})
|
|
438
|
+
|
|
439
|
+
lines.push('', 'MailDev:')
|
|
440
|
+
instanceMetadata.maildev.forEach(({ title, text }) => {
|
|
441
|
+
lines.push(` ${stripStyle(title)}: ${stripStyle(text)}`)
|
|
442
|
+
})
|
|
443
|
+
|
|
444
|
+
lines.push('')
|
|
445
|
+
if (!anyRunning) {
|
|
446
|
+
lines.push(
|
|
447
|
+
'Environment is not running. Start it with: magento-scripts start'
|
|
448
|
+
)
|
|
449
|
+
}
|
|
450
|
+
lines.push(
|
|
451
|
+
'Run Magento CLI: magento-scripts exec php bin/magento <command>'
|
|
452
|
+
)
|
|
453
|
+
|
|
454
|
+
logger.log(lines.join('\n'))
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
module.exports = { prettyStatus, simpleStatus }
|
|
@@ -30,7 +30,7 @@ const buildTheme = ({ themePath }) => ({
|
|
|
30
30
|
task.output = t
|
|
31
31
|
}
|
|
32
32
|
})
|
|
33
|
-
} catch
|
|
33
|
+
} catch {
|
|
34
34
|
throw new UnknownError(`We were unable to install theme dependencies in ${themePath} using ${logger.style.code(
|
|
35
35
|
commandToInstallDependencies
|
|
36
36
|
)} command!
|
package/lib/util/analytics.js
CHANGED
|
@@ -168,7 +168,7 @@ class Analytics {
|
|
|
168
168
|
|
|
169
169
|
try {
|
|
170
170
|
this.setClientIdentifier(generateUUID())
|
|
171
|
-
} catch
|
|
171
|
+
} catch {
|
|
172
172
|
this.setClientIdentifier(`${Date.now()}`)
|
|
173
173
|
}
|
|
174
174
|
}
|
|
@@ -233,7 +233,7 @@ class Analytics {
|
|
|
233
233
|
analyticsParameters.session = {
|
|
234
234
|
ipOverride: await getExternalIpAddress()
|
|
235
235
|
}
|
|
236
|
-
} catch
|
|
236
|
+
} catch {
|
|
237
237
|
// Do nothing
|
|
238
238
|
}
|
|
239
239
|
|
package/lib/util/ip.js
CHANGED
package/lib/util/is-wsl.js
CHANGED
package/lib/util/path-exists.js
CHANGED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enable nginx host → store mapping only when multiple stores use distinct domains.
|
|
3
|
+
* Duplicate hostnames produce invalid nginx map directives.
|
|
4
|
+
*
|
|
5
|
+
* @param {Record<string, string> | undefined} storeDomains
|
|
6
|
+
* @returns {boolean}
|
|
7
|
+
*/
|
|
8
|
+
const shouldUseStoreDomainMapping = (storeDomains) => {
|
|
9
|
+
if (!storeDomains) {
|
|
10
|
+
return false
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const entries = Object.entries(storeDomains)
|
|
14
|
+
|
|
15
|
+
if (entries.length <= 1) {
|
|
16
|
+
return false
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const domains = entries.map(([, domain]) => domain)
|
|
20
|
+
|
|
21
|
+
return new Set(domains).size === domains.length
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Map store/website entities to domains from storeDomains, falling back to admin
|
|
26
|
+
* for scopes not explicitly configured (e.g. website "base" on single-domain setups).
|
|
27
|
+
*
|
|
28
|
+
* @param {Record<string, string>} storeDomains
|
|
29
|
+
* @param {{ code: string }[]} entities
|
|
30
|
+
* @param {'website_id' | 'store_id'} idField
|
|
31
|
+
* @returns {Record<string, { scopeId: number, domain: string }>}
|
|
32
|
+
*/
|
|
33
|
+
const resolveStoreDomainsForScopes = (storeDomains, entities, idField) => {
|
|
34
|
+
const defaultDomain = storeDomains.admin
|
|
35
|
+
|
|
36
|
+
/** @type {Record<string, { scopeId: number, domain: string }>} */
|
|
37
|
+
const mapped = Object.entries(storeDomains).reduce(
|
|
38
|
+
(acc, [key, val]) => {
|
|
39
|
+
const entity = entities.find((entity) => entity.code === key)
|
|
40
|
+
|
|
41
|
+
if (entity) {
|
|
42
|
+
return {
|
|
43
|
+
...acc,
|
|
44
|
+
[entity.code]: {
|
|
45
|
+
scopeId: entity[idField],
|
|
46
|
+
domain: val
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return acc
|
|
52
|
+
},
|
|
53
|
+
{}
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
for (const entity of entities) {
|
|
57
|
+
if (!mapped[entity.code]) {
|
|
58
|
+
mapped[entity.code] = {
|
|
59
|
+
scopeId: entity[idField],
|
|
60
|
+
domain: defaultDomain
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return mapped
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = {
|
|
69
|
+
shouldUseStoreDomainMapping,
|
|
70
|
+
resolveStoreDomainsForScopes
|
|
71
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const assert = require('assert')
|
|
2
|
+
const {
|
|
3
|
+
shouldUseStoreDomainMapping,
|
|
4
|
+
resolveStoreDomainsForScopes
|
|
5
|
+
} = require('./store-domains')
|
|
6
|
+
|
|
7
|
+
assert.strictEqual(shouldUseStoreDomainMapping(undefined), false)
|
|
8
|
+
assert.strictEqual(shouldUseStoreDomainMapping({ admin: 'localhost' }), false)
|
|
9
|
+
assert.strictEqual(
|
|
10
|
+
shouldUseStoreDomainMapping({
|
|
11
|
+
admin: 'shop.local',
|
|
12
|
+
base: 'shop.local'
|
|
13
|
+
}),
|
|
14
|
+
false
|
|
15
|
+
)
|
|
16
|
+
assert.strictEqual(
|
|
17
|
+
shouldUseStoreDomainMapping({
|
|
18
|
+
admin: 'admin.local',
|
|
19
|
+
base: 'shop.local'
|
|
20
|
+
}),
|
|
21
|
+
true
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
const websites = [
|
|
25
|
+
{ website_id: 0, code: 'admin' },
|
|
26
|
+
{ website_id: 1, code: 'base' }
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
const resolved = resolveStoreDomainsForScopes(
|
|
30
|
+
{ admin: 'jollyes.local' },
|
|
31
|
+
websites,
|
|
32
|
+
'website_id'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
assert.deepStrictEqual(resolved, {
|
|
36
|
+
admin: { scopeId: 0, domain: 'jollyes.local' },
|
|
37
|
+
base: { scopeId: 1, domain: 'jollyes.local' }
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
assert.deepStrictEqual(
|
|
41
|
+
resolveStoreDomainsForScopes(
|
|
42
|
+
{ admin: 'jollyes.local', base: 'other.local' },
|
|
43
|
+
websites,
|
|
44
|
+
'website_id'
|
|
45
|
+
),
|
|
46
|
+
{
|
|
47
|
+
admin: { scopeId: 0, domain: 'jollyes.local' },
|
|
48
|
+
base: { scopeId: 1, domain: 'other.local' }
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
console.log('store-domains.test.js: ok')
|
package/lib/util/systemctl.js
CHANGED
|
@@ -66,7 +66,7 @@ const systemctlControl = (serviceName, defaultOptions = {}) => ({
|
|
|
66
66
|
...options
|
|
67
67
|
})
|
|
68
68
|
return result.includes('enabled')
|
|
69
|
-
} catch
|
|
69
|
+
} catch {
|
|
70
70
|
return false
|
|
71
71
|
}
|
|
72
72
|
},
|
|
@@ -81,7 +81,7 @@ const systemctlControl = (serviceName, defaultOptions = {}) => ({
|
|
|
81
81
|
})
|
|
82
82
|
|
|
83
83
|
return result.includes('active (running)')
|
|
84
|
-
} catch
|
|
84
|
+
} catch {
|
|
85
85
|
return false
|
|
86
86
|
}
|
|
87
87
|
},
|
|
@@ -101,7 +101,7 @@ const systemctlControl = (serviceName, defaultOptions = {}) => ({
|
|
|
101
101
|
const result = await execAsyncSpawn(command)
|
|
102
102
|
|
|
103
103
|
return result === '1'
|
|
104
|
-
} catch
|
|
104
|
+
} catch {
|
|
105
105
|
return false
|
|
106
106
|
}
|
|
107
107
|
}
|
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.
|
|
6
|
+
"version": "2.4.13",
|
|
7
7
|
"main": "./index.js",
|
|
8
8
|
"types": "./typings/index.d.ts",
|
|
9
9
|
"license": "OSL-3.0",
|
|
@@ -59,5 +59,9 @@
|
|
|
59
59
|
"@types/node": "^20.14.11",
|
|
60
60
|
"@types/yargs": "^17.0.32"
|
|
61
61
|
},
|
|
62
|
-
"
|
|
62
|
+
"scripts": {
|
|
63
|
+
"lint": "oxlint",
|
|
64
|
+
"test:store-domains": "node lib/util/store-domains.test.js"
|
|
65
|
+
},
|
|
66
|
+
"gitHead": "a6d5ac78bf24f1ebd5b725248bb775e3e7c3d3ef"
|
|
63
67
|
}
|
package/.eslintrc
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
|
|
4
|
-
const AGENTS_MD_CONTENT = `# CMA (magento-scripts) — AI/CI Reference
|
|
5
|
-
|
|
6
|
-
> Auto-generated by magento-scripts. Do not delete.
|
|
7
|
-
|
|
8
|
-
## Critical
|
|
9
|
-
|
|
10
|
-
- **Cannot run as root** — exits immediately with code 1, no override.
|
|
11
|
-
- **Non-TTY safe** — silent renderer activates automatically in CI/pipes; no \`-q\` needed.
|
|
12
|
-
- **Do NOT use \`--\` with exec** — it gets passed as the command and fails. Correct: \`magento-scripts exec php bin/magento cache:flush\`
|
|
13
|
-
- **\`cli\` is TTY-only** — use \`exec php bin/magento <cmd>\` in automation instead.
|
|
14
|
-
- **\`import-db\` is self-contained** — it stops running containers, assigns ports, starts services, waits for MariaDB, and imports. Do NOT run \`start\` before or between \`import-db\` attempts — that creates port conflicts. Just run \`import-db\` directly.
|
|
15
|
-
- **Do NOT run \`start\` then \`import-db\`** — each command manages its own container lifecycle. Running both creates split-brain port assignments. Use one or the other.
|
|
16
|
-
- **Long-running commands** — \`start\` and \`import-db\` can take 10+ minutes (container setup, large dumps). Set timeouts to at least 600000ms (10 min) or run without a timeout.
|
|
17
|
-
- **Shell escaping** — Avoid \`!\` in SQL strings passed via \`exec\` (e.g., \`!=\`), as the shell may interpret it. Use SQL alternatives like \`<>\` instead of \`!=\`.
|
|
18
|
-
|
|
19
|
-
## Commands
|
|
20
|
-
|
|
21
|
-
| Command | What it does | Key flags |
|
|
22
|
-
|---------|-------------|-----------|
|
|
23
|
-
| \`start\` | Start Docker environment | \`--no-open\`, \`--skip-setup\`, \`--port\` |
|
|
24
|
-
| \`stop\` | Stop all containers | — |
|
|
25
|
-
| \`status\` | Show container/DB status | — |
|
|
26
|
-
| \`exec <container> [cmd...]\` | Run command in container | use \`--\` before flags |
|
|
27
|
-
| \`import-db [file]\` | Import SQL dump into MariaDB | \`-y\` (non-interactive), \`--remote-db=ssh://user@host\` |
|
|
28
|
-
| \`logs <container>\` | Stream container logs | \`--tail N\`, \`--follow\` |
|
|
29
|
-
| \`cleanup\` | Remove cached/generated files | \`--force\` |
|
|
30
|
-
| \`cli\` | Interactive shell (TTY only) | — |
|
|
31
|
-
| \`link <path>\` | Link ScandiPWA theme | — |
|
|
32
|
-
|
|
33
|
-
## Containers
|
|
34
|
-
|
|
35
|
-
\`php\`, \`phpWithXdebug\`, \`nginx\`, \`sslTerminator\`, \`redis\`, \`mariadb\`, \`elasticsearch\`, \`maildev\`, \`varnish\` (if enabled)
|
|
36
|
-
|
|
37
|
-
## Examples
|
|
38
|
-
|
|
39
|
-
\`\`\`bash
|
|
40
|
-
magento-scripts start --no-open --skip-setup
|
|
41
|
-
magento-scripts import-db dump.sql -y
|
|
42
|
-
magento-scripts import-db -y --remote-db=ssh://user@host
|
|
43
|
-
magento-scripts exec php bin/magento cache:flush
|
|
44
|
-
magento-scripts exec php bin/magento indexer:reindex
|
|
45
|
-
|
|
46
|
-
# Query MariaDB (use mariadb binary, not mysql)
|
|
47
|
-
magento-scripts exec mariadb mariadb -u magento -pmagento magento -e "SELECT COUNT(*) FROM store"
|
|
48
|
-
magento-scripts logs magento --tail 100
|
|
49
|
-
magento-scripts stop
|
|
50
|
-
\`\`\`
|
|
51
|
-
`
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Write AGENTS.md and CLAUDE.md to the current working directory if they do not already exist.
|
|
55
|
-
* This gives AI agents and CI pipelines a command reference for the project.
|
|
56
|
-
*/
|
|
57
|
-
function ensureAgentsMd() {
|
|
58
|
-
const cwd = process.cwd()
|
|
59
|
-
|
|
60
|
-
const agentsDest = path.join(cwd, 'AGENTS.md')
|
|
61
|
-
if (!fs.existsSync(agentsDest)) {
|
|
62
|
-
try {
|
|
63
|
-
fs.writeFileSync(agentsDest, AGENTS_MD_CONTENT, 'utf8')
|
|
64
|
-
} catch (e) {
|
|
65
|
-
// Non-fatal — silently skip if the directory is not writable
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const claudeDest = path.join(cwd, 'CLAUDE.md')
|
|
70
|
-
if (!fs.existsSync(claudeDest)) {
|
|
71
|
-
try {
|
|
72
|
-
fs.writeFileSync(claudeDest, '@AGENTS.md\n', 'utf8')
|
|
73
|
-
} catch (e) {
|
|
74
|
-
// Non-fatal
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
module.exports = ensureAgentsMd
|