Skip to content

Commit 078784e

Browse files
dmerandgonzaloriestra
authored andcommitted
Preserve theme info multi-environment JSON
Assisted-By: devx/8b520d2e-bc34-4a3d-b5fa-748b2fff362e
1 parent a2aeae2 commit 078784e

14 files changed

Lines changed: 279 additions & 699 deletions

File tree

‎.changeset/theme-info-json-schema.md‎

Lines changed: 0 additions & 5 deletions
This file was deleted.

‎packages/cli-kit/src/public/node/base-command.test.ts‎

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import Command from './base-command.js'
22
import {Environments} from './environments.js'
33
import {encodeToml as encodeTOML} from './toml/codec.js'
44
import {globalFlags, jsonFlag, requiredIfNonInteractive} from './cli.js'
5-
import {emitCommandEvent} from './command-events.js'
5+
import {emitCommandEvent, runWithCommandEvents} from './command-events.js'
66
import {inTemporaryDirectory, mkdir, writeFile} from './fs.js'
77
import {joinPath, resolvePath, cwd} from './path.js'
88
import {mockAndCaptureOutput} from './testing/output.js'
99
import {unstyled} from './output.js'
1010
import {defineJsonOutputSchema} from './json-output-schema.js'
1111
import {zod} from './schema.js'
1212
import {afterEach, beforeEach, describe, expect, test, vi} from 'vitest'
13-
import {Flags} from '@oclif/core'
13+
import {Config, Flags} from '@oclif/core'
1414
import {Ajv} from 'ajv'
1515

1616
let originalStdinIsTTY: boolean | undefined
@@ -54,6 +54,7 @@ class MockCommand extends Command {
5454
default: 'default stringy',
5555
}),
5656
password: Flags.string({}),
57+
'store-password': Flags.string({}),
5758
environment: Flags.string({
5859
multiple: true,
5960
default: [],
@@ -220,6 +221,11 @@ const environmentWithPassword = {
220221
password: 'password',
221222
}
222223

224+
const environmentWithCredentials = {
225+
password: 'admin-password',
226+
'store-password': 'storefront-secret',
227+
}
228+
223229
const allEnvironments: Environments = {
224230
environments: {
225231
validEnvironment,
@@ -232,6 +238,7 @@ const allEnvironments: Environments = {
232238
environmentMatchingDefault,
233239
environmentWithDefaultOverride,
234240
environmentWithPassword,
241+
environmentWithCredentials,
235242
},
236243
}
237244

@@ -754,6 +761,31 @@ describe('applying environments', async () => {
754761
`)
755762
})
756763

764+
runTestInTmpDir('reports environment settings as JSON diagnostics with masked passwords', async (tmpDir) => {
765+
const sink = vi.fn()
766+
const output = mockAndCaptureOutput()
767+
output.clear()
768+
await runWithCommandEvents({outputMode: 'json', sink}, async () => {
769+
const config = new Config({root: __dirname})
770+
await config.load()
771+
const command = new MockCommand(['--path', tmpDir, '--environment', 'environmentWithCredentials'], config)
772+
await command.run()
773+
})
774+
775+
expect(sink).toHaveBeenCalledWith(
776+
expect.objectContaining({
777+
type: 'diagnostic',
778+
level: 'info',
779+
message:
780+
'Using applicable flags from environmentWithCredentials environment:\npassword: ********word\nstore-password: ********cret',
781+
}),
782+
)
783+
for (const credential of Object.values(environmentWithCredentials)) {
784+
expect(JSON.stringify(sink.mock.calls)).not.toContain(credential)
785+
}
786+
expect(output.info()).toBe('')
787+
})
788+
757789
runTestInTmpDir('reports environment settings with masked passwords', async (tmpDir: string) => {
758790
// Given
759791
const outputMock = mockAndCaptureOutput()

‎packages/cli-kit/src/public/node/base-command.ts‎

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {isDevelopment} from './context/local.js'
22
import {addPublicMetadata} from './metadata.js'
33
import {AbortError} from './error.js'
4-
import {runWithCommandEventsForCommand} from './command-events.js'
4+
import {commandEventOutputMode, emitCommandEvent, runWithCommandEventsForCommand} from './command-events.js'
55
import {outputContent, outputResult, outputToken} from './output.js'
66
import {setCurrentSessionAlias} from './session.js'
77
import {terminalSupportsPrompting} from './system.js'
@@ -145,7 +145,7 @@ abstract class BaseCommand extends Command {
145145
result = await this.resultWithEnvironment<TFlags, TGlobalFlags, TArgs>(result, options, argv)
146146
await setCurrentSessionAlias(result.flags['auth-alias'])
147147
await addFromParsedFlags(result.flags)
148-
this.failMissingNonTTYFlagRequirements(result.flags, this.applicableNonTTYFlagRequirements(result.flags))
148+
this.validateNonTTYFlags(result.flags)
149149
return {...result, ...{argv: result.argv as string[]}}
150150
}
151151

@@ -154,6 +154,10 @@ abstract class BaseCommand extends Command {
154154
return undefined
155155
}
156156

157+
protected validateNonTTYFlags(flags: FlagOutput): void {
158+
this.failMissingNonTTYFlagRequirements(flags, this.applicableNonTTYFlagRequirements(flags))
159+
}
160+
157161
protected failMissingNonTTYFlags(flags: FlagOutput, requiredFlags: string[]): void {
158162
this.failMissingNonTTYFlagRequirements(
159163
flags,
@@ -322,13 +326,21 @@ function reportEnvironmentApplication<
322326
const userSpecifiedThisFlag = Object.prototype.hasOwnProperty.call(noDefaultsFlags, name)
323327
const environmentContainsFlag = Object.prototype.hasOwnProperty.call(environment, name)
324328
if (!userSpecifiedThisFlag && environmentContainsFlag) {
325-
const valueToReport = name === 'password' ? `********${value.substr(-4)}` : value
329+
const valueToReport = name === 'password' || name === 'store-password' ? `********${value.substr(-4)}` : value
326330
changes[name] = valueToReport
327331
}
328332
}
329333
if (Object.keys(changes).length === 0) return
330334

331335
const items = Object.entries(changes).map(([name, value]) => `${name}: ${value}`)
336+
if (commandEventOutputMode() === 'json') {
337+
emitCommandEvent({
338+
type: 'diagnostic',
339+
level: 'info',
340+
message: `Using applicable flags from ${environmentName} environment:\n${items.join('\n')}`,
341+
})
342+
return
343+
}
332344
// eslint-disable-next-line no-void
333345
void import('./ui.js').then(({renderInfo}) => {
334346
renderInfo({

‎packages/cli/README.md‎

Lines changed: 1 addition & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8023,12 +8023,10 @@ DESCRIPTION
80238023
Displays information about your theme environment, including your current store. Can also retrieve information about a
80248024
specific theme.
80258025

8026-
Use `--json` for machine-readable output.
8026+
Use `--json-schema` to print the result, error, and event schemas.
80278027

80288028
Output from `--json` conforms to the `ThemeInfoResult` schema.
80298029

8030-
Use `--json-schema` to print the result, error, and event schemas.
8031-
80328030
```json
80338031
{
80348032
"anyOf": [
@@ -8037,9 +8035,6 @@ DESCRIPTION
80378035
},
80388036
{
80398037
"$ref": "#/definitions/ThemeEnvironmentInfo"
8040-
},
8041-
{
8042-
"$ref": "#/definitions/ThemeInfoMultiEnvironmentResult"
80438038
}
80448039
],
80458040
"title": "ThemeInfoResult",
@@ -8122,44 +8117,6 @@ DESCRIPTION
81228117
"node_version"
81238118
],
81248119
"additionalProperties": false
8125-
},
8126-
"ThemeInfoMultiEnvironmentEntry": {
8127-
"type": "object",
8128-
"properties": {
8129-
"environment": {
8130-
"type": "string"
8131-
},
8132-
"result": {
8133-
"anyOf": [
8134-
{
8135-
"$ref": "#/definitions/ThemeInfoThemeResult"
8136-
},
8137-
{
8138-
"$ref": "#/definitions/ThemeEnvironmentInfo"
8139-
}
8140-
]
8141-
}
8142-
},
8143-
"required": [
8144-
"environment",
8145-
"result"
8146-
],
8147-
"additionalProperties": false
8148-
},
8149-
"ThemeInfoMultiEnvironmentResult": {
8150-
"type": "object",
8151-
"properties": {
8152-
"environments": {
8153-
"type": "array",
8154-
"items": {
8155-
"$ref": "#/definitions/ThemeInfoMultiEnvironmentEntry"
8156-
}
8157-
}
8158-
},
8159-
"required": [
8160-
"environments"
8161-
],
8162-
"additionalProperties": false
81638120
}
81648121
},
81658122
"$schema": "http://json-schema.org/draft-07/schema#"

‎packages/cli/oclif.manifest.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10124,8 +10124,8 @@
1012410124
"args": {
1012510125
},
1012610126
"customPluginName": "@shopify/theme",
10127-
"description": "Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme.\n\nUse `--json` for machine-readable output.\n\nOutput from `--json` conforms to the `ThemeInfoResult` schema.\n\nUse `--json-schema` to print the result, error, and event schemas.\n\n```json\n{\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/ThemeInfoThemeResult\"\n },\n {\n \"$ref\": \"#/definitions/ThemeEnvironmentInfo\"\n },\n {\n \"$ref\": \"#/definitions/ThemeInfoMultiEnvironmentResult\"\n }\n ],\n \"title\": \"ThemeInfoResult\",\n \"definitions\": {\n \"ThemeInfoTheme\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"number\"\n },\n \"name\": {\n \"type\": \"string\"\n },\n \"role\": {\n \"type\": \"string\"\n },\n \"shop\": {\n \"type\": \"string\"\n },\n \"preview_url\": {\n \"type\": \"string\"\n },\n \"editor_url\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\",\n \"role\",\n \"shop\",\n \"preview_url\",\n \"editor_url\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeInfoThemeResult\": {\n \"type\": \"object\",\n \"properties\": {\n \"theme\": {\n \"$ref\": \"#/definitions/ThemeInfoTheme\"\n }\n },\n \"required\": [\n \"theme\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeEnvironmentInfo\": {\n \"type\": \"object\",\n \"properties\": {\n \"store\": {\n \"type\": \"string\"\n },\n \"development_theme_id\": {\n \"type\": [\n \"number\",\n \"null\"\n ]\n },\n \"cli_version\": {\n \"type\": \"string\"\n },\n \"os\": {\n \"type\": \"string\"\n },\n \"shell\": {\n \"type\": \"string\"\n },\n \"node_version\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"store\",\n \"development_theme_id\",\n \"cli_version\",\n \"os\",\n \"shell\",\n \"node_version\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeInfoMultiEnvironmentEntry\": {\n \"type\": \"object\",\n \"properties\": {\n \"environment\": {\n \"type\": \"string\"\n },\n \"result\": {\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/ThemeInfoThemeResult\"\n },\n {\n \"$ref\": \"#/definitions/ThemeEnvironmentInfo\"\n }\n ]\n }\n },\n \"required\": [\n \"environment\",\n \"result\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeInfoMultiEnvironmentResult\": {\n \"type\": \"object\",\n \"properties\": {\n \"environments\": {\n \"type\": \"array\",\n \"items\": {\n \"$ref\": \"#/definitions/ThemeInfoMultiEnvironmentEntry\"\n }\n }\n },\n \"required\": [\n \"environments\"\n ],\n \"additionalProperties\": false\n }\n },\n \"$schema\": \"http://json-schema.org/draft-07/schema#\"\n}\n```",
10128-
"descriptionWithMarkdown": "Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme.\n\nUse `--json` for machine-readable output.",
10127+
"description": "Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme.\n\nUse `--json-schema` to print the result, error, and event schemas.\n\nOutput from `--json` conforms to the `ThemeInfoResult` schema.\n\n```json\n{\n \"anyOf\": [\n {\n \"$ref\": \"#/definitions/ThemeInfoThemeResult\"\n },\n {\n \"$ref\": \"#/definitions/ThemeEnvironmentInfo\"\n }\n ],\n \"title\": \"ThemeInfoResult\",\n \"definitions\": {\n \"ThemeInfoTheme\": {\n \"type\": \"object\",\n \"properties\": {\n \"id\": {\n \"type\": \"number\"\n },\n \"name\": {\n \"type\": \"string\"\n },\n \"role\": {\n \"type\": \"string\"\n },\n \"shop\": {\n \"type\": \"string\"\n },\n \"preview_url\": {\n \"type\": \"string\"\n },\n \"editor_url\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"id\",\n \"name\",\n \"role\",\n \"shop\",\n \"preview_url\",\n \"editor_url\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeInfoThemeResult\": {\n \"type\": \"object\",\n \"properties\": {\n \"theme\": {\n \"$ref\": \"#/definitions/ThemeInfoTheme\"\n }\n },\n \"required\": [\n \"theme\"\n ],\n \"additionalProperties\": false\n },\n \"ThemeEnvironmentInfo\": {\n \"type\": \"object\",\n \"properties\": {\n \"store\": {\n \"type\": \"string\"\n },\n \"development_theme_id\": {\n \"type\": [\n \"number\",\n \"null\"\n ]\n },\n \"cli_version\": {\n \"type\": \"string\"\n },\n \"os\": {\n \"type\": \"string\"\n },\n \"shell\": {\n \"type\": \"string\"\n },\n \"node_version\": {\n \"type\": \"string\"\n }\n },\n \"required\": [\n \"store\",\n \"development_theme_id\",\n \"cli_version\",\n \"os\",\n \"shell\",\n \"node_version\"\n ],\n \"additionalProperties\": false\n }\n },\n \"$schema\": \"http://json-schema.org/draft-07/schema#\"\n}\n```",
10128+
"descriptionWithMarkdown": "Displays information about your theme environment, including your current store. Can also retrieve information about a specific theme.",
1012910129
"enableJsonFlag": false,
1013010130
"flags": {
1013110131
"auth-alias": {

0 commit comments

Comments
 (0)