Skip to content

Commit 3ed06dc

Browse files
committed
Merge remote-tracking branch 'origin/main' into t/commerce/W-20926594/refactor-fix-lookup-config
2 parents 8b09254 + 269de20 commit 3ed06dc

86 files changed

Lines changed: 5000 additions & 254 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@salesforce/b2c-cli': minor
3+
---
4+
5+
Add `setup config` command to display resolved configuration with source tracking.
6+
7+
Shows all configuration values organized by category (Instance, Authentication, SCAPI, MRT) and indicates which source file or environment variable provided each value. Sensitive values are masked by default; use `--unmask` to reveal them.

docs/cli/setup.md

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,107 @@
11
---
2-
description: Commands for installing AI agent skills for Claude Code, Cursor, Windsurf, and other agentic IDEs.
2+
description: Commands for viewing configuration, installing AI agent skills, and setting up the development environment.
33
---
44

55
# Setup Commands
66

7-
Commands for setting up the development environment with AI agent skills.
7+
Commands for viewing configuration and setting up the development environment.
8+
9+
## b2c setup config
10+
11+
Display the resolved configuration from all sources, showing which values are set and where they came from. Useful for debugging configuration issues.
12+
13+
### Usage
14+
15+
```bash
16+
b2c setup config [FLAGS]
17+
```
18+
19+
### Flags
20+
21+
| Flag | Description | Default |
22+
|------|-------------|---------|
23+
| `--unmask` | Show sensitive values unmasked (passwords, secrets, API keys) | `false` |
24+
| `--json` | Output results as JSON | `false` |
25+
26+
### Examples
27+
28+
```bash
29+
# Display resolved configuration (sensitive values masked)
30+
b2c setup config
31+
32+
# Display configuration with sensitive values unmasked
33+
b2c setup config --unmask
34+
35+
# Output as JSON for scripting
36+
b2c setup config --json
37+
38+
# Debug configuration with a specific instance
39+
b2c setup config -i staging
40+
```
41+
42+
### Output
43+
44+
The command displays configuration organized by category:
45+
46+
- **Instance**: hostname, webdavHostname, codeVersion
47+
- **Authentication (Basic)**: username, password
48+
- **Authentication (OAuth)**: clientId, clientSecret, scopes, authMethods, accountManagerHost
49+
- **SCAPI**: shortCode
50+
- **Managed Runtime (MRT)**: mrtProject, mrtEnvironment, mrtApiKey, mrtOrigin
51+
- **Metadata**: instanceName
52+
- **Sources**: List of configuration sources that contributed values
53+
54+
Each value shows its source in brackets (e.g., `[dw.json]`, `[SFCC_CLIENT_ID]`, `[~/.mobify]`).
55+
56+
Example output:
57+
58+
```
59+
Configuration
60+
────────────────────────────────────────────────────────────
61+
62+
Instance
63+
hostname my-sandbox.dx.commercecloud.salesforce.com [DwJsonSource]
64+
webdavHostname -
65+
codeVersion version1 [DwJsonSource]
66+
67+
Authentication (Basic)
68+
username admin [DwJsonSource]
69+
password admi...REDACTED [DwJsonSource]
70+
71+
Authentication (OAuth)
72+
clientId my-client-id [password-store]
73+
clientSecret my-c...REDACTED [password-store]
74+
scopes -
75+
authMethods -
76+
accountManagerHost -
77+
78+
SCAPI
79+
shortCode abc123 [DwJsonSource]
80+
81+
Managed Runtime (MRT)
82+
mrtProject my-project [MobifySource]
83+
mrtApiKey mrtk...REDACTED [MobifySource]
84+
85+
Sources
86+
────────────────────────────────────────────────────────────
87+
1. DwJsonSource /path/to/project/dw.json
88+
2. MobifySource /Users/user/.mobify
89+
3. password-store pass:b2c-cli/_default
90+
```
91+
92+
### Sensitive Values
93+
94+
By default, sensitive fields are masked to prevent accidental exposure:
95+
96+
- `password` - Basic auth access key
97+
- `clientSecret` - OAuth client secret
98+
- `mrtApiKey` - MRT API key
99+
100+
Use `--unmask` to reveal the actual values when needed for debugging.
101+
102+
### See Also
103+
104+
- [Configuration Guide](/guide/configuration) - How to configure the CLI
8105

9106
## b2c setup skills
10107

docs/guide/configuration.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,29 @@ SFCC_AUTH_METHODS=client-credentials,implicit b2c code deploy
264264

265265
The CLI will try each method in order until one succeeds.
266266

267+
## Debugging Configuration
268+
269+
Use `b2c setup config` to view the resolved configuration and see which source provided each value:
270+
271+
```bash
272+
# Display resolved configuration (sensitive values masked)
273+
b2c setup config
274+
275+
# Show actual sensitive values
276+
b2c setup config --unmask
277+
278+
# Output as JSON
279+
b2c setup config --json
280+
```
281+
282+
This command helps troubleshoot issues like:
283+
- Verifying which configuration file is being used
284+
- Checking if environment variables are being read
285+
- Understanding credential source priority
286+
- Identifying hostname mismatch protection triggers
287+
288+
See [setup config](/cli/setup#b2c-setup-config) for full documentation.
289+
267290
## Next Steps
268291

269292
- [CLI Reference](/cli/) - Browse available commands

packages/b2c-cli/.mocharc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"reporter": "spec",
77
"timeout": 60000,
88
"node-option": [
9-
"import=tsx"
9+
"import=tsx",
10+
"conditions=development"
1011
]
1112
}

packages/b2c-cli/eslint.config.mjs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import headerPlugin from 'eslint-plugin-header';
99
import path from 'node:path';
1010
import {fileURLToPath} from 'node:url';
1111

12-
import {copyrightHeader, sharedRules, oclifRules, prettierPlugin} from '../../eslint.config.mjs';
12+
import {copyrightHeader, sharedRules, oclifRules, chaiTestRules, prettierPlugin} from '../../eslint.config.mjs';
1313

1414
const gitignorePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '.gitignore');
1515
headerPlugin.rules.header.meta.schema = false;
@@ -19,7 +19,7 @@ export default [
1919
// node_modules must be explicitly ignored because the .gitignore pattern only covers
2020
// packages/b2c-cli/node_modules, not the monorepo root node_modules
2121
{
22-
ignores: ['**/node_modules/**', 'test/functional/fixtures/**/*.js'],
22+
ignores: ['**/node_modules/**', 'test/functional/fixtures/**/*.js', '**/node_modules/marked-terminal/**'],
2323
},
2424
includeIgnoreFile(gitignorePath),
2525
...oclif,
@@ -39,4 +39,29 @@ export default [
3939
...oclifRules,
4040
},
4141
},
42+
{
43+
files: ['test/**/*.ts'],
44+
rules: {
45+
...chaiTestRules,
46+
// Tests use stubbing patterns that intentionally return undefined
47+
'unicorn/no-useless-undefined': 'off',
48+
// Some tests use void 0 to satisfy TS stub typings; allow it in tests
49+
'no-void': 'off',
50+
// Command tests frequently use `any` to avoid over-typing oclif command internals
51+
'@typescript-eslint/no-explicit-any': 'off',
52+
// Helper functions in tests are commonly declared within suites for clarity
53+
'unicorn/consistent-function-scoping': 'off',
54+
// Sinon default import is intentional and idiomatic in tests
55+
'import/no-named-as-default-member': 'off',
56+
// import/namespace behaves inconsistently across environments when parsing CJS modules like marked-terminal
57+
'import/namespace': 'off',
58+
},
59+
},
60+
{
61+
files: ['src/commands/docs/**/*.ts'],
62+
rules: {
63+
// marked-terminal is CJS and breaks import/namespace static analysis
64+
'import/namespace': 'off',
65+
},
66+
},
4267
];

packages/b2c-cli/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
"oclif": "^4",
4646
"prettier": "^3.6.2",
4747
"shx": "^0.3.3",
48+
"sinon": "^21.0.1",
4849
"tsx": "^4.20.6",
4950
"typescript": "^5"
5051
},

packages/b2c-cli/src/commands/code/delete.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
5151
}),
5252
};
5353

54+
protected operations = {
55+
confirm,
56+
deleteCodeVersion,
57+
};
58+
5459
async run(): Promise<void> {
5560
this.requireOAuthCredentials();
5661

@@ -59,7 +64,7 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
5964

6065
// Confirm deletion unless --force is used
6166
if (!this.flags.force) {
62-
const confirmed = await confirm(
67+
const confirmed = await this.operations.confirm(
6368
t(
6469
'commands.code.delete.confirm',
6570
'Are you sure you want to delete code version "{{codeVersion}}" on {{hostname}}? (y/n)',
@@ -80,7 +85,7 @@ export default class CodeDelete extends InstanceCommand<typeof CodeDelete> {
8085
}),
8186
);
8287

83-
await deleteCodeVersion(this.instance, codeVersion);
88+
await this.operations.deleteCodeVersion(this.instance, codeVersion);
8489
this.log(t('commands.code.delete.deleted', 'Code version {{codeVersion}} deleted successfully', {codeVersion}));
8590
}
8691
}

packages/b2c-cli/src/commands/code/deploy.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
4747
}),
4848
};
4949

50+
protected operations = {
51+
uploadCartridges,
52+
deleteCartridges,
53+
getActiveCodeVersion,
54+
reloadCodeVersion,
55+
};
56+
5057
async run(): Promise<DeployResult> {
5158
this.requireWebDavCredentials();
5259
this.requireOAuthCredentials();
@@ -59,7 +66,7 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
5966
this.warn(
6067
t('commands.code.deploy.noCodeVersion', 'No code version specified, discovering active code version...'),
6168
);
62-
const activeVersion = await getActiveCodeVersion(this.instance);
69+
const activeVersion = await this.operations.getActiveCodeVersion(this.instance);
6370
if (!activeVersion?.id) {
6471
this.error(
6572
t('commands.code.deploy.noActiveVersion', 'No active code version found. Specify one with --code-version.'),
@@ -119,17 +126,17 @@ export default class CodeDeploy extends CartridgeCommand<typeof CodeDeploy> {
119126
try {
120127
// Optionally delete existing cartridges first
121128
if (this.flags.delete) {
122-
await deleteCartridges(this.instance, cartridges);
129+
await this.operations.deleteCartridges(this.instance, cartridges);
123130
}
124131

125132
// Upload cartridges
126-
await uploadCartridges(this.instance, cartridges);
133+
await this.operations.uploadCartridges(this.instance, cartridges);
127134

128135
// Optionally reload code version
129136
let reloaded = false;
130137
if (this.flags.reload) {
131138
try {
132-
await reloadCodeVersion(this.instance, version);
139+
await this.operations.reloadCodeVersion(this.instance, version);
133140
reloaded = true;
134141
} catch (error) {
135142
this.logger?.debug(`Could not reload code version: ${error instanceof Error ? error.message : error}`);

packages/b2c-cli/src/commands/code/watch.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
2727
...CartridgeCommand.cartridgeFlags,
2828
};
2929

30+
protected operations = {
31+
watchCartridges,
32+
};
33+
3034
async run(): Promise<void> {
3135
this.requireWebDavCredentials();
3236
this.requireOAuthCredentials();
@@ -41,7 +45,7 @@ export default class CodeWatch extends CartridgeCommand<typeof CodeWatch> {
4145
}
4246

4347
try {
44-
const result = await watchCartridges(this.instance, this.cartridgePath, {
48+
const result = await this.operations.watchCartridges(this.instance, this.cartridgePath, {
4549
...this.cartridgeOptions,
4650
onUpload: (files) => {
4751
this.log(t('commands.code.watch.uploaded', '[UPLOAD] {{count}} file(s)', {count: files.length}));

packages/b2c-cli/src/commands/docs/download.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ export default class DocsDownload extends InstanceCommand<typeof DocsDownload> {
3737
}),
3838
};
3939

40+
protected operations = {
41+
downloadDocs,
42+
};
43+
4044
async run(): Promise<DownloadDocsResult> {
4145
this.requireServer();
4246
this.requireWebDavCredentials();
@@ -50,7 +54,7 @@ export default class DocsDownload extends InstanceCommand<typeof DocsDownload> {
5054
}),
5155
);
5256

53-
const result = await downloadDocs(this.instance, {
57+
const result = await this.operations.downloadDocs(this.instance, {
5458
outputDir,
5559
keepArchive,
5660
});

0 commit comments

Comments
 (0)