Skip to content

Commit 6d33c07

Browse files
authored
fix: revert MSO changes (#1192)
* Revert "fix: bump multi-stage-output" This reverts commit 36ed590. * Revert "Merge pull request #1120 from salesforcecli/mdonnalley/ink" This reverts commit bfc34ef, reversing changes made to 3499c53.
1 parent a977171 commit 6d33c07

File tree

8 files changed

+216
-382
lines changed

8 files changed

+216
-382
lines changed

package.json

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66
"bugs": "https://github.com/forcedotcom/cli/issues",
77
"dependencies": {
88
"@oclif/core": "^4.0.16",
9-
"@oclif/multi-stage-output": "^0.3.1",
109
"@salesforce/core": "^8.4.0",
1110
"@salesforce/kit": "^3.2.0",
1211
"@salesforce/sf-plugins-core": "^11.3.3",
1312
"@salesforce/source-deploy-retrieve": "^12.5.1",
1413
"ansis": "^3.2.0",
1514
"change-case": "^5.4.4",
1615
"is-wsl": "^3.1.0",
17-
"open": "^10.1.0",
18-
"terminal-link": "^3.0.0"
16+
"open": "^10.1.0"
1917
},
2018
"devDependencies": {
2119
"@oclif/plugin-command-snapshot": "^5.2.14",

src/commands/org/create/scratch.ts

Lines changed: 16 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -5,28 +5,25 @@
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';
98
import {
109
Lifecycle,
1110
Messages,
1211
Org,
1312
scratchOrgCreate,
1413
ScratchOrgLifecycleEvent,
1514
scratchOrgLifecycleEventName,
16-
scratchOrgLifecycleStages,
1715
SfError,
1816
} from '@salesforce/core';
1917
import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
2018
import { Duration } from '@salesforce/kit';
21-
import terminalLink from 'terminal-link';
2219
import { buildScratchOrgRequest } from '../../../shared/scratchOrgRequest.js';
20+
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2321
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
2422

2523
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2624
const messages = Messages.loadMessages('@salesforce/plugin-org', 'create_scratch');
2725

2826
const definitionFileHelpGroupName = 'Definition File Override';
29-
3027
export default class OrgCreateScratch extends SfCommand<ScratchCreateResponse> {
3128
public static readonly summary = messages.getMessage('summary');
3229
public static readonly description = messages.getMessage('description');
@@ -170,69 +167,38 @@ export default class OrgCreateScratch extends SfCommand<ScratchCreateResponse> {
170167
flags,
171168
flags['client-id'] ? await this.secretPrompt({ message: messages.getMessage('prompt.secret') }) : undefined
172169
);
170+
let lastStatus: string | undefined;
173171

174-
const stager = new MultiStageOutput<ScratchOrgLifecycleEvent & { alias: string | undefined }>({
175-
stages: flags.async ? ['prepare request', 'send request', 'done'] : scratchOrgLifecycleStages,
176-
title: flags.async ? 'Creating Scratch Org (async)' : 'Creating Scratch Org',
177-
data: { alias: flags.alias },
178-
jsonEnabled: this.jsonEnabled(),
179-
postStagesBlock: [
180-
{
181-
label: 'Request Id',
182-
type: 'dynamic-key-value',
183-
get: (data) =>
184-
data?.scratchOrgInfo?.Id && terminalLink(data.scratchOrgInfo.Id, `${baseUrl}/${data.scratchOrgInfo.Id}`),
185-
bold: true,
186-
},
187-
{
188-
label: 'OrgId',
189-
type: 'dynamic-key-value',
190-
get: (data) => data?.scratchOrgInfo?.ScratchOrg,
191-
bold: true,
192-
color: 'cyan',
193-
},
194-
{
195-
label: 'Username',
196-
type: 'dynamic-key-value',
197-
get: (data) => data?.scratchOrgInfo?.SignupUsername,
198-
bold: true,
199-
color: 'cyan',
200-
},
201-
{
202-
label: 'Alias',
203-
type: 'static-key-value',
204-
get: (data) => data?.alias,
205-
},
206-
],
207-
});
208-
209-
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
210-
stager.goto(data.stage, data);
211-
if (data.stage === 'done') {
212-
stager.stop();
213-
}
214-
return Promise.resolve();
215-
});
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+
);
216183

217184
try {
218185
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgCreate(createCommandOptions);
219186

187+
this.spinner.stop(lastStatus);
220188
if (!scratchOrgInfo) {
221189
throw new SfError('The scratch org did not return with any information');
222190
}
223-
191+
this.log();
224192
if (flags.async) {
225-
stager.goto('done', { scratchOrgInfo });
226-
stager.stop();
227193
this.info(messages.getMessage('action.resume', [this.config.bin, scratchOrgInfo.Id]));
228194
} else {
229195
this.logSuccess(messages.getMessage('success'));
230196
}
231197

232198
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
233199
} catch (error) {
234-
stager.stop(error as Error);
235200
if (error instanceof SfError && error.name === 'ScratchOrgInfoTimeoutError') {
201+
this.spinner.stop(lastStatus);
236202
const scratchOrgInfoId = (error.data as { scratchOrgInfoId: string }).scratchOrgInfoId;
237203
const resumeMessage = messages.getMessage('action.resume', [this.config.bin, scratchOrgInfoId]);
238204

src/commands/org/resume/scratch.ts

Lines changed: 10 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,11 @@ import {
1414
ScratchOrgCache,
1515
ScratchOrgLifecycleEvent,
1616
scratchOrgLifecycleEventName,
17-
scratchOrgLifecycleStages,
1817
scratchOrgResume,
1918
SfError,
2019
} from '@salesforce/core';
21-
import terminalLink from 'terminal-link';
22-
import { MultiStageOutput } from '@oclif/multi-stage-output';
2320
import { ScratchCreateResponse } from '../../../shared/orgTypes.js';
21+
import { buildStatus } from '../../../shared/scratchOrgOutput.js';
2422

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

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

9861
lifecycle.on<ScratchOrgLifecycleEvent>(scratchOrgLifecycleEventName, async (data): Promise<void> => {
99-
stager.goto(data.stage, data);
100-
if (data.stage === 'done') {
101-
stager.stop();
102-
}
62+
lastStatus = buildStatus(data, hubBaseUrl);
63+
this.spinner.status = lastStatus;
10364
return Promise.resolve();
10465
});
10566

67+
this.log();
68+
this.spinner.start('Creating Scratch Org');
69+
10670
try {
10771
const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgResume(jobId);
72+
this.spinner.stop(lastStatus);
73+
10874
this.log();
10975
this.logSuccess(messages.getMessage('success'));
11076
return { username, scratchOrgInfo, authFields, warnings, orgId: authFields?.orgId };
11177
} catch (e) {
112-
stager.stop(e as Error);
113-
11478
if (cache.keys() && e instanceof Error && e.name === 'CacheMissError') {
11579
// we have something in the cache, but it didn't match what the user passed in
11680
throw messages.createError('error.jobIdMismatch', [jobId]);

src/shared/scratchOrgOutput.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) 2020, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import { ScratchOrgLifecycleEvent, scratchOrgLifecycleStages } from '@salesforce/core';
8+
import ansis from 'ansis';
9+
import { capitalCase } from 'change-case';
10+
import { StandardColors } from '@salesforce/sf-plugins-core';
11+
12+
const boldBlue = (input: string): string => ansis.rgb(81, 176, 235).bold(input);
13+
const boldPurple = (input: string): string => ansis.rgb(157, 129, 221).bold(input);
14+
15+
export const buildStatus = (data: ScratchOrgLifecycleEvent, baseUrl: string): string => `
16+
RequestId: ${formatRequest(baseUrl, data.scratchOrgInfo?.Id)}
17+
OrgId: ${formatOrgId(data.scratchOrgInfo?.ScratchOrg)}
18+
Username: ${formatUsername(data.scratchOrgInfo?.SignupUsername)}
19+
${formatStage(data.stage)}`;
20+
21+
export const formatStage = (currentStage: ScratchOrgLifecycleEvent['stage']): string =>
22+
scratchOrgLifecycleStages
23+
.map((stage, stageIndex) => {
24+
// current stage
25+
if (currentStage === stage) return formatCurrentStage(stage);
26+
// completed stages
27+
if (scratchOrgLifecycleStages.indexOf(currentStage) > stageIndex) return formatCompletedStage(stage);
28+
// future stage
29+
return formatFutureStage(stage);
30+
})
31+
.join('\n');
32+
33+
export const formatRequest = (baseUrl: string, id?: string): string =>
34+
`${id ? `${ansis.bold(id)} (${baseUrl}/${id})` : ''}`;
35+
36+
export const formatUsername = (username?: string): string => `${username ? `${boldBlue(username)} ` : ''}`;
37+
export const formatOrgId = (id?: string): string => `${id ? `${boldBlue(id)} ` : ''}`;
38+
39+
export const formatCurrentStage = (stage: string): string => boldPurple(capitalCase(stage));
40+
export const formatCompletedStage = (stage: string): string => StandardColors.success.bold(`✓ ${capitalCase(stage)}`);
41+
export const formatFutureStage = (stage: string): string => StandardColors.info(capitalCase(stage));

test/shared/scratchOrgOutput.test.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright (c) 2020, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import { scratchOrgLifecycleStages, ScratchOrgInfo } from '@salesforce/core';
8+
import { expect } from 'chai';
9+
import {
10+
buildStatus,
11+
formatCompletedStage,
12+
formatCurrentStage,
13+
formatFutureStage,
14+
formatOrgId,
15+
formatRequest,
16+
formatStage,
17+
formatUsername,
18+
} from '../../src/shared/scratchOrgOutput.js';
19+
20+
describe('human output', () => {
21+
describe('stage formatter', () => {
22+
it('highlights 1st stage with color, remaining are dimmed', () => {
23+
const result = formatStage(scratchOrgLifecycleStages[0]);
24+
expect(result).includes(formatCurrentStage(scratchOrgLifecycleStages[0]));
25+
scratchOrgLifecycleStages.slice(1).forEach((stage) => {
26+
expect(result).includes(formatFutureStage(stage));
27+
});
28+
});
29+
it('highlights other stage with color and previous as green', () => {
30+
const result = formatStage(scratchOrgLifecycleStages[1]);
31+
expect(result).includes(formatCurrentStage(scratchOrgLifecycleStages[1]));
32+
expect(result).includes(formatCompletedStage(scratchOrgLifecycleStages[0]));
33+
scratchOrgLifecycleStages.slice(2).forEach((stage) => {
34+
expect(result).includes(formatFutureStage(stage));
35+
});
36+
});
37+
});
38+
describe('overall output', () => {
39+
const baseUrl = 'https://ut.my.salesforce.com';
40+
const scratchOrgInfo: ScratchOrgInfo = {
41+
Id: '2SR3u0000008VBXGA2',
42+
ScratchOrg: '00D0x000000Lf9E',
43+
SignupUsername: 'test-uluqhj7qu9k2@example.com',
44+
LoginUrl: 'https://ut.my.salesforce.com',
45+
AuthCode: 'x',
46+
Username: 'test-uluqhj7qu9k2@example.com',
47+
SignupEmail: 'x',
48+
Status: 'Active',
49+
SignupInstance: '',
50+
};
51+
52+
it('shows all fields when all data is present', () => {
53+
const output = buildStatus(
54+
{ stage: scratchOrgLifecycleStages[scratchOrgLifecycleStages.length - 1], scratchOrgInfo },
55+
baseUrl
56+
);
57+
expect(output).includes(`RequestId: ${formatRequest(baseUrl, scratchOrgInfo.Id)}`);
58+
expect(output).includes(`OrgId: ${formatOrgId(scratchOrgInfo.ScratchOrg)}`);
59+
expect(output).includes(`Username: ${formatUsername(scratchOrgInfo.SignupUsername)}`);
60+
expect(output).not.includes('}');
61+
expect(output).not.includes('{');
62+
});
63+
64+
it('still shows all field names when no data is present', () => {
65+
const output = buildStatus({ stage: scratchOrgLifecycleStages[0] }, baseUrl);
66+
expect(output).includes('RequestId: ');
67+
expect(output).includes('OrgId: ');
68+
expect(output).includes('RequestId: ');
69+
});
70+
});
71+
});

test/shared/scratchOrgRequest.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ import { Config, Interfaces } from '@oclif/core';
88
import { expect } from 'chai';
99
import { MockTestOrgData, TestContext } from '@salesforce/core/testSetup';
1010
import { buildScratchOrgRequest } from '../../src/shared/scratchOrgRequest.js';
11-
import OrgCreateScratch from '../../src/commands/org/create/scratch.js';
11+
import EnvCreateScratch from '../../src/commands/org/create/scratch.js';
1212

13-
class Wrapper extends OrgCreateScratch {
13+
class Wrapper extends EnvCreateScratch {
1414
// simple method to return the parsed flags so they can be used in the tests
1515
public async getFlags(): Promise<Interfaces.InferredFlags<typeof Wrapper.flags>> {
1616
return (await this.parse(Wrapper)).flags;
1717
}
1818
}
1919

2020
/** pass in the params in array form, get back the parsed flags */
21-
const paramsToFlags = async (params: string[]): Promise<Interfaces.InferredFlags<typeof OrgCreateScratch.flags>> =>
21+
const paramsToFlags = async (params: string[]): Promise<Interfaces.InferredFlags<typeof EnvCreateScratch.flags>> =>
2222
new Wrapper(params, { runHook: () => ({ successes: [], failures: [] }) } as unknown as Config).getFlags();
2323

2424
describe('buildScratchOrgRequest function', () => {

test/unit/org/resumeScratch.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ describe('org:resume:scratch', () => {
3434
});
3535

3636
try {
37-
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO', '--json']);
37+
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO']);
3838
expect(false, 'ResumeSandbox should have thrown sandboxCreateNotComplete');
3939
} catch (err: unknown) {
4040
const error = err as SfError;
@@ -50,7 +50,7 @@ describe('org:resume:scratch', () => {
5050
});
5151

5252
try {
53-
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO', '--json']);
53+
await OrgResumeScratch.run(['--job-id', '2SRFOOFOOFOOFOOFOO']);
5454
expect(false, 'ResumeSandbox should have thrown sandboxCreateNotComplete');
5555
} catch (err: unknown) {
5656
const error = err as SfError;

0 commit comments

Comments
 (0)