Skip to content

Commit 58a25fc

Browse files
Merge pull request #1203 from salesforcecli/prerelease/beta
feat: multi-stage output
2 parents 0e7d3a7 + a96a017 commit 58a25fc

File tree

9 files changed

+379
-202
lines changed

9 files changed

+379
-202
lines changed

README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ FLAG DESCRIPTIONS
229229
sandbox.
230230
```
231231

232-
_See code: [src/commands/org/create/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/create/sandbox.ts)_
232+
_See code: [src/commands/org/create/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/create/sandbox.ts)_
233233

234234
## `sf org create scratch`
235235

@@ -383,7 +383,7 @@ FLAG DESCRIPTIONS
383383
Omit this flag to have Salesforce generate a unique username for your org.
384384
```
385385

386-
_See code: [src/commands/org/create/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/create/scratch.ts)_
386+
_See code: [src/commands/org/create/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/create/scratch.ts)_
387387

388388
## `sf org delete sandbox`
389389

@@ -429,7 +429,7 @@ EXAMPLES
429429
$ sf org delete sandbox --target-org my-sandbox --no-prompt
430430
```
431431

432-
_See code: [src/commands/org/delete/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/delete/sandbox.ts)_
432+
_See code: [src/commands/org/delete/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/delete/sandbox.ts)_
433433

434434
## `sf org delete scratch`
435435

@@ -473,7 +473,7 @@ EXAMPLES
473473
$ sf org delete scratch --target-org my-scratch-org --no-prompt
474474
```
475475

476-
_See code: [src/commands/org/delete/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/delete/scratch.ts)_
476+
_See code: [src/commands/org/delete/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/delete/scratch.ts)_
477477

478478
## `sf org disable tracking`
479479

@@ -512,7 +512,7 @@ EXAMPLES
512512
$ sf org disable tracking
513513
```
514514

515-
_See code: [src/commands/org/disable/tracking.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/disable/tracking.ts)_
515+
_See code: [src/commands/org/disable/tracking.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/disable/tracking.ts)_
516516

517517
## `sf org display`
518518

@@ -557,7 +557,7 @@ EXAMPLES
557557
$ sf org display --target-org TestOrg1 --verbose
558558
```
559559

560-
_See code: [src/commands/org/display.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/display.ts)_
560+
_See code: [src/commands/org/display.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/display.ts)_
561561

562562
## `sf org enable tracking`
563563

@@ -599,7 +599,7 @@ EXAMPLES
599599
$ sf org enable tracking
600600
```
601601

602-
_See code: [src/commands/org/enable/tracking.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/enable/tracking.ts)_
602+
_See code: [src/commands/org/enable/tracking.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/enable/tracking.ts)_
603603

604604
## `sf org list`
605605

@@ -638,7 +638,7 @@ EXAMPLES
638638
$ sf org list --clean
639639
```
640640

641-
_See code: [src/commands/org/list.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/list.ts)_
641+
_See code: [src/commands/org/list.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/list.ts)_
642642

643643
## `sf org list metadata`
644644

@@ -705,7 +705,7 @@ FLAG DESCRIPTIONS
705705
Examples of metadata types that use folders are Dashboard, Document, EmailTemplate, and Report.
706706
```
707707

708-
_See code: [src/commands/org/list/metadata.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/list/metadata.ts)_
708+
_See code: [src/commands/org/list/metadata.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/list/metadata.ts)_
709709

710710
## `sf org list metadata-types`
711711

@@ -760,7 +760,7 @@ FLAG DESCRIPTIONS
760760
Override the api version used for api requests made by this command
761761
```
762762

763-
_See code: [src/commands/org/list/metadata-types.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/list/metadata-types.ts)_
763+
_See code: [src/commands/org/list/metadata-types.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/list/metadata-types.ts)_
764764

765765
## `sf org open`
766766

@@ -832,7 +832,7 @@ EXAMPLES
832832
$ sf org open --source-file force-app/main/default/flows/Hello.flow-meta.xml
833833
```
834834

835-
_See code: [src/commands/org/open.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/open.ts)_
835+
_See code: [src/commands/org/open.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/open.ts)_
836836

837837
## `sf org refresh sandbox`
838838

@@ -909,7 +909,7 @@ FLAG DESCRIPTIONS
909909
By default, a sandbox auto-activates after a refresh. Use this flag to control sandbox activation manually.
910910
```
911911

912-
_See code: [src/commands/org/refresh/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/refresh/sandbox.ts)_
912+
_See code: [src/commands/org/refresh/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/refresh/sandbox.ts)_
913913

914914
## `sf org resume sandbox`
915915

@@ -972,7 +972,7 @@ FLAG DESCRIPTIONS
972972
returns the job ID. To resume checking the sandbox creation, rerun this command.
973973
```
974974

975-
_See code: [src/commands/org/resume/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/resume/sandbox.ts)_
975+
_See code: [src/commands/org/resume/sandbox.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/resume/sandbox.ts)_
976976

977977
## `sf org resume scratch`
978978

@@ -1019,6 +1019,6 @@ FLAG DESCRIPTIONS
10191019
The job ID is valid for 24 hours after you start the scratch org creation.
10201020
```
10211021

1022-
_See code: [src/commands/org/resume/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.2/src/commands/org/resume/scratch.ts)_
1022+
_See code: [src/commands/org/resume/scratch.ts](https://github.com/salesforcecli/plugin-org/blob/4.6.3-beta.0/src/commands/org/resume/scratch.ts)_
10231023

10241024
<!-- commandsstop -->

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
11
{
22
"name": "@salesforce/plugin-org",
33
"description": "Commands to interact with Salesforce orgs",
4-
"version": "4.6.2",
4+
"version": "4.6.3-beta.3",
55
"author": "Salesforce",
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
88
"@oclif/core": "^4.0.28",
9+
"@oclif/multi-stage-output": "^0.7.7",
910
"@salesforce/core": "^8.6.1",
1011
"@salesforce/kit": "^3.2.3",
1112
"@salesforce/sf-plugins-core": "^11.3.12",
1213
"@salesforce/source-deploy-retrieve": "^12.7.1",
1314
"ansis": "^3.2.0",
1415
"change-case": "^5.4.4",
1516
"is-wsl": "^3.1.0",
16-
"open": "^10.1.0"
17+
"open": "^10.1.0",
18+
"terminal-link": "^3.0.0"
1719
},
1820
"devDependencies": {
1921
"@oclif/plugin-command-snapshot": "^5.2.18",

src/commands/org/create/scratch.ts

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,29 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8+
import { MultiStageOutput } from '@oclif/multi-stage-output';
89
import {
910
Lifecycle,
1011
Messages,
1112
Org,
1213
scratchOrgCreate,
1314
ScratchOrgLifecycleEvent,
1415
scratchOrgLifecycleEventName,
16+
scratchOrgLifecycleStages,
1517
SfError,
1618
} from '@salesforce/core';
1719
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
1820
import { Duration } from '@salesforce/kit';
21+
import terminalLink from 'terminal-link';
22+
import { capitalCase } from 'change-case';
1923
import { buildScratchOrgRequest } from '../../../shared/scratchOrgRequest.js';
20-
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2124
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
2225

2326
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2427
const messages = Messages.loadMessages('@salesforce/plugin-org', 'create_scratch');
2528

2629
const definitionFileHelpGroupName = 'Definition File Override';
30+
2731
export default class OrgCreateScratch extends SfCommand<ScratchCreateResponse> {
2832
public static readonly summary = messages.getMessage('summary');
2933
public static readonly description = messages.getMessage('description');
@@ -167,38 +171,71 @@ export default class OrgCreateScratch extends SfCommand<ScratchCreateResponse> {
167171
flags,
168172
flags['client-id'] ? await this.secretPrompt({ message: messages.getMessage('prompt.secret') }) : undefined
169173
);
170-
let lastStatus: string | undefined;
171174

172-
if (!flags.async) {
173-
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
174-
lastStatus = buildStatus(data, baseUrl);
175-
this.spinner.status = lastStatus;
176-
return Promise.resolve();
177-
});
178-
}
179-
this.log();
180-
this.spinner.start(
181-
flags.async ? 'Requesting Scratch Org (will not wait for completion because --async)' : 'Creating Scratch Org'
182-
);
175+
const mso = new MultiStageOutput<ScratchOrgLifecycleEvent & { alias: string | undefined }>({
176+
stages: (flags.async ? ['prepare request', 'send request', 'done'] : scratchOrgLifecycleStages).map((stage) =>
177+
capitalCase(stage)
178+
),
179+
title: flags.async ? 'Creating Scratch Org (async)' : 'Creating Scratch Org',
180+
data: { alias: flags.alias },
181+
jsonEnabled: this.jsonEnabled(),
182+
postStagesBlock: [
183+
{
184+
label: 'Request Id',
185+
type: 'dynamic-key-value',
186+
get: (data) =>
187+
data?.scratchOrgInfo?.Id && terminalLink(data.scratchOrgInfo.Id, `${baseUrl}/${data.scratchOrgInfo.Id}`),
188+
bold: true,
189+
},
190+
{
191+
label: 'OrgId',
192+
type: 'dynamic-key-value',
193+
get: (data) => data?.scratchOrgInfo?.ScratchOrg,
194+
bold: true,
195+
color: 'cyan',
196+
},
197+
{
198+
label: 'Username',
199+
type: 'dynamic-key-value',
200+
get: (data) => data?.scratchOrgInfo?.SignupUsername,
201+
bold: true,
202+
color: 'cyan',
203+
},
204+
{
205+
label: 'Alias',
206+
type: 'static-key-value',
207+
get: (data) => data?.alias,
208+
},
209+
],
210+
});
211+
212+
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
213+
mso.skipTo(capitalCase(data.stage), data);
214+
if (data.stage === 'done') {
215+
mso.stop();
216+
}
217+
return Promise.resolve();
218+
});
183219

184220
try {
185221
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgCreate(createCommandOptions);
186222

187-
this.spinner.stop(lastStatus);
188223
if (!scratchOrgInfo) {
189224
throw new SfError('The scratch org did not return with any information');
190225
}
191-
this.log();
226+
192227
if (flags.async) {
228+
mso.skipTo('Done', { scratchOrgInfo });
229+
mso.stop();
193230
this.info(messages.getMessage('action.resume', [this.config.bin, scratchOrgInfo.Id]));
194231
} else {
195232
this.logSuccess(messages.getMessage('success'));
196233
}
197234

198235
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
199236
} catch (error) {
237+
mso.error();
200238
if (error instanceof SfError && error.name === 'ScratchOrgInfoTimeoutError') {
201-
this.spinner.stop(lastStatus);
202239
const scratchOrgInfoId = (error.data as { scratchOrgInfoId: string }).scratchOrgInfoId;
203240
const resumeMessage = messages.getMessage('action.resume', [this.config.bin, scratchOrgInfoId]);
204241

src/commands/org/resume/scratch.ts

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@ import {
1414
ScratchOrgCache,
1515
ScratchOrgLifecycleEvent,
1616
scratchOrgLifecycleEventName,
17+
scratchOrgLifecycleStages,
1718
scratchOrgResume,
1819
SfError,
1920
} from '@salesforce/core';
21+
import terminalLink from 'terminal-link';
22+
import { MultiStageOutput } from '@oclif/multi-stage-output';
23+
import { capitalCase } from 'change-case';
2024
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
21-
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2225

2326
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2427
const messages = Messages.loadMessages('@salesforce/plugin-org', 'resume_scratch');
@@ -55,26 +58,60 @@ export default class OrgResumeScratch extends SfCommand<ScratchCreateResponse> {
5558

5659
// oclif doesn't know that the exactlyOne flag will ensure that one of these is set, and there we definitely have a jobID.
5760
assert(jobId);
58-
const hubBaseUrl = cache.get(jobId)?.hubBaseUrl;
59-
let lastStatus: string | undefined;
61+
const cached = cache.get(jobId);
62+
const hubBaseUrl = cached?.hubBaseUrl;
63+
64+
const mso = new MultiStageOutput<ScratchOrgLifecycleEvent & { alias: string | undefined }>({
65+
stages: scratchOrgLifecycleStages.map((stage) => capitalCase(stage)),
66+
title: 'Resuming Scratch Org',
67+
data: { alias: cached?.alias },
68+
jsonEnabled: this.jsonEnabled(),
69+
postStagesBlock: [
70+
{
71+
label: 'Request Id',
72+
type: 'dynamic-key-value',
73+
get: (data) =>
74+
data?.scratchOrgInfo?.Id && terminalLink(data.scratchOrgInfo.Id, `${hubBaseUrl}/${data.scratchOrgInfo.Id}`),
75+
bold: true,
76+
},
77+
{
78+
label: 'OrgId',
79+
type: 'dynamic-key-value',
80+
get: (data) => data?.scratchOrgInfo?.ScratchOrg,
81+
bold: true,
82+
color: 'cyan',
83+
},
84+
{
85+
label: 'Username',
86+
type: 'dynamic-key-value',
87+
get: (data) => data?.scratchOrgInfo?.SignupUsername,
88+
bold: true,
89+
color: 'cyan',
90+
},
91+
{
92+
label: 'Alias',
93+
type: 'static-key-value',
94+
get: (data) => data?.alias,
95+
},
96+
],
97+
});
6098

6199
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
62-
lastStatus = buildStatus(data, hubBaseUrl);
63-
this.spinner.status = lastStatus;
100+
mso.skipTo(capitalCase(data.stage), data);
101+
if (data.stage === 'done') {
102+
mso.stop();
103+
}
64104
return Promise.resolve();
65105
});
66106

67-
this.log();
68-
this.spinner.start('Creating Scratch Org');
69-
70107
try {
71108
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgResume(jobId);
72-
this.spinner.stop(lastStatus);
73-
74109
this.log();
75110
this.logSuccess(messages.getMessage('success'));
76111
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
77112
} catch (e) {
113+
mso.error();
114+
78115
if (cache.keys() && e instanceof Error && e.name === 'CacheMissError') {
79116
// we have something in the cache, but it didn't match what the user passed in
80117
throw messages.createError('error.jobIdMismatch', [jobId]);

src/shared/scratchOrgOutput.ts

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

0 commit comments

Comments
 (0)