Skip to content

Commit 43f18ff

Browse files
committed
Use base -e/--environment flag as clone source instead of separate --from
Drop the redundant --from flag on b2c mrt env clone. Every MrtCommand already has a base --environment / -e flag (with MRT_ENVIRONMENT and dw.json mrtEnvironment fallbacks) that selects the working environment; using it as the source for clone keeps the behavior consistent with the rest of the env commands. The positional argument is the new slug. Also reject identical source and destination slugs.
1 parent c5596e5 commit 43f18ff

4 files changed

Lines changed: 60 additions & 24 deletions

File tree

docs/cli/mrt.md

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -318,21 +318,23 @@ b2c mrt env delete old-env -p my-storefront --force
318318

319319
Clone an environment from an existing source environment. The new target receives the source's configuration (excluding proxies and the production flag) and is automatically deployed with the source target's current bundle (if any). Optionally clones redirects, environment variables, and B2C target info.
320320

321+
The source environment is the one selected by `--environment` / `-e` (or `MRT_ENVIRONMENT` / `mrtEnvironment` in `dw.json`). The positional argument is the **new** environment's slug.
322+
321323
```bash
322-
# Clone an environment with default settings
323-
b2c mrt env clone staging-copy --from staging --project my-storefront
324+
# Clone the configured environment into a new slug
325+
b2c mrt env clone staging-copy -p my-storefront -e staging
324326

325327
# Clone with redirects and environment variables
326-
b2c mrt env clone qa --from staging -p my-storefront --clone-redirects --clone-env-vars
328+
b2c mrt env clone qa -p my-storefront -e staging --clone-redirects --clone-env-vars
327329

328330
# Clone using a custom domain certificate
329-
b2c mrt env clone qa --from staging -p my-storefront \
331+
b2c mrt env clone qa -p my-storefront -e staging \
330332
--external-hostname qa.example.com --certificate-id 123 --wait
331333
```
332334

333335
| Flag | Description |
334336
|------|-------------|
335-
| `--from`, `-f` | Source environment slug to clone from (required) |
337+
| `--environment`, `-e` | Source environment slug (defaults to `mrtEnvironment` / `MRT_ENVIRONMENT`) |
336338
| `--external-hostname` | Full external hostname (required for non-MRT-managed certificates) |
337339
| `--external-domain` | External domain for Universal PWA SSR |
338340
| `--certificate-id` | Certificate ID for custom domain (use `b2c mrt org cert list` to find) |

packages/b2c-cli/src/commands/mrt/env/clone.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,13 @@ export default class MrtEnvClone extends MrtCommand<typeof MrtEnvClone> {
5454
static enableJsonFlag = true;
5555

5656
static examples = [
57-
'<%= config.bin %> <%= command.id %> staging-copy --from staging --project my-storefront',
58-
'<%= config.bin %> <%= command.id %> qa --from staging -p my-storefront --clone-redirects --clone-env-vars',
59-
'<%= config.bin %> <%= command.id %> qa --from staging -p my-storefront --external-hostname qa.example.com --certificate-id 123 --wait',
57+
'<%= config.bin %> <%= command.id %> staging-copy -p my-storefront -e staging',
58+
'<%= config.bin %> <%= command.id %> qa -p my-storefront -e staging --clone-redirects --clone-env-vars',
59+
'<%= config.bin %> <%= command.id %> qa -p my-storefront -e staging --external-hostname qa.example.com --certificate-id 123 --wait',
6060
];
6161

6262
static flags = {
6363
...MrtCommand.baseFlags,
64-
from: Flags.string({
65-
char: 'f',
66-
description: 'Source environment slug to clone from',
67-
required: true,
68-
}),
6964
'external-hostname': Flags.string({
7065
description: 'Full external hostname for the new environment (required for non-MRT-managed certs)',
7166
}),
@@ -113,14 +108,21 @@ export default class MrtEnvClone extends MrtCommand<typeof MrtEnvClone> {
113108
this.requireMrtCredentials();
114109

115110
const {slug} = this.args;
116-
const {mrtProject: project} = this.resolvedConfig.values;
111+
const {mrtProject: project, mrtEnvironment: fromSlug} = this.resolvedConfig.values;
117112

118113
if (!project) {
119114
this.error('MRT project is required. Provide --project flag, set MRT_PROJECT, or set mrtProject in dw.json.');
120115
}
116+
if (!fromSlug) {
117+
this.error(
118+
'Source environment is required. Provide --environment / -e, set MRT_ENVIRONMENT, or set mrtEnvironment in dw.json.',
119+
);
120+
}
121+
if (fromSlug === slug) {
122+
this.error(`Source and destination environment slugs must differ (both are "${slug}").`);
123+
}
121124

122125
const {
123-
from: fromSlug,
124126
'external-hostname': externalHostname,
125127
'external-domain': externalDomain,
126128
'certificate-id': certificateId,

packages/b2c-cli/test/commands/mrt/env/clone.test.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ describe('mrt env clone', () => {
3535

3636
it('errors when project is missing', async () => {
3737
const command = createCommand();
38-
stubParse(command, {from: 'staging'}, {slug: 'staging-copy'});
38+
stubParse(command, {}, {slug: 'staging-copy'});
3939
await command.init();
4040
stubCommonAuth(command);
41-
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: undefined}}));
41+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: undefined, mrtEnvironment: 'staging'}}));
4242
const errorStub = sinon.stub(command, 'error').throws(new Error('expected'));
4343

4444
try {
@@ -49,12 +49,11 @@ describe('mrt env clone', () => {
4949
}
5050
});
5151

52-
it('passes flags through to cloneEnv and returns the new env', async () => {
52+
it('uses mrtEnvironment as the source and passes flags through to cloneEnv', async () => {
5353
const command = createCommand();
5454
stubParse(
5555
command,
5656
{
57-
from: 'staging',
5857
'external-hostname': 'qa.example.com',
5958
'certificate-id': 123,
6059
'clone-redirects': true,
@@ -68,7 +67,9 @@ describe('mrt env clone', () => {
6867
stubCommonAuth(command);
6968
sinon.stub(command, 'jsonEnabled').returns(true);
7069
sinon.stub(command, 'log').returns(void 0);
71-
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: 'p', mrtOrigin: 'https://example.com'}}));
70+
sinon
71+
.stub(command, 'resolvedConfig')
72+
.get(() => ({values: {mrtProject: 'p', mrtEnvironment: 'staging', mrtOrigin: 'https://example.com'}}));
7273

7374
const cloneStub = sinon.stub().resolves({slug: 'qa', name: 'qa', state: 'CREATE_IN_PROGRESS'} as any);
7475
const waitStub = sinon.stub();
@@ -90,12 +91,43 @@ describe('mrt env clone', () => {
9091
expect(result.slug).to.equal('qa');
9192
});
9293

94+
it('errors when mrtEnvironment is not set', async () => {
95+
const command = createCommand();
96+
stubParse(command, {'clone-redirects': false, 'clone-env-vars': false, 'clone-b2c-info': false}, {slug: 'qa'});
97+
await command.init();
98+
stubCommonAuth(command);
99+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: 'p'}}));
100+
const errorStub = sinon.stub(command, 'error').throws(new Error('expected'));
101+
102+
try {
103+
await command.run();
104+
expect.fail('expected error');
105+
} catch {
106+
expect(errorStub.firstCall.args[0]).to.include('Source environment is required');
107+
}
108+
});
109+
110+
it('errors when source and destination slugs are equal', async () => {
111+
const command = createCommand();
112+
stubParse(command, {'clone-redirects': false, 'clone-env-vars': false, 'clone-b2c-info': false}, {slug: 'staging'});
113+
await command.init();
114+
stubCommonAuth(command);
115+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: 'p', mrtEnvironment: 'staging'}}));
116+
const errorStub = sinon.stub(command, 'error').throws(new Error('expected'));
117+
118+
try {
119+
await command.run();
120+
expect.fail('expected error');
121+
} catch {
122+
expect(errorStub.firstCall.args[0]).to.include('must differ');
123+
}
124+
});
125+
93126
it('waits for env when --wait is set', async () => {
94127
const command = createCommand();
95128
stubParse(
96129
command,
97130
{
98-
from: 'staging',
99131
wait: true,
100132
'poll-interval': 1,
101133
timeout: 30,
@@ -109,7 +141,7 @@ describe('mrt env clone', () => {
109141
stubCommonAuth(command);
110142
sinon.stub(command, 'jsonEnabled').returns(true);
111143
sinon.stub(command, 'log').returns(void 0);
112-
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: 'p'}}));
144+
sinon.stub(command, 'resolvedConfig').get(() => ({values: {mrtProject: 'p', mrtEnvironment: 'staging'}}));
113145

114146
const cloneStub = sinon.stub().resolves({slug: 'qa', state: 'CREATE_IN_PROGRESS'} as any);
115147
const waitStub = sinon.stub().resolves({slug: 'qa', state: 'ACTIVE'} as any);

skills/b2c-cli/skills/b2c-mrt/SKILL.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ b2c mrt env list -p my-storefront
5151
# Create a new environment
5252
b2c mrt env create qa -p my-storefront --name "QA Environment"
5353

54-
# Clone an existing environment (optionally copy redirects, env vars, B2C info)
55-
b2c mrt env clone qa -p my-storefront --from staging --clone-redirects --clone-env-vars
54+
# Clone an existing environment (-e is the source; positional arg is the new slug)
55+
b2c mrt env clone qa -p my-storefront -e staging --clone-redirects --clone-env-vars
5656

5757
# Get environment details
5858
b2c mrt env get -p my-storefront -e production

0 commit comments

Comments
 (0)