UNPKG

@salesforce/plugin-org

Version:

Commands to interact with Salesforce orgs

155 lines 6.86 kB
/* * Copyright 2026, Salesforce, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { strict as assert } from 'node:assert'; import { Flags, SfCommand } from '@salesforce/sf-plugins-core'; import { envVars, Lifecycle, Messages, ScratchOrgCache, scratchOrgLifecycleEventName, scratchOrgLifecycleStages, scratchOrgResume, SfError, } from '@salesforce/core'; import terminalLink from 'terminal-link'; import { MultiStageOutput } from '@oclif/multi-stage-output'; import { capitalCase } from 'change-case'; import { omit } from '@salesforce/kit'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('@salesforce/plugin-org', 'resume_scratch'); const secretsMessages = Messages.loadMessages('@salesforce/plugin-org', 'secrets-redacted'); export default class OrgResumeScratch extends SfCommand { static summary = messages.getMessage('summary'); static description = messages.getMessage('description'); static examples = messages.getMessages('examples'); static aliases = ['env:resume:scratch']; static deprecateAliases = true; static flags = { 'job-id': Flags.salesforceId({ char: 'i', length: 'both', summary: messages.getMessage('flags.job-id.summary'), description: messages.getMessage('flags.job-id.description'), exactlyOne: ['use-most-recent', 'job-id'], startsWith: '2SR', }), 'use-most-recent': Flags.boolean({ char: 'r', summary: messages.getMessage('flags.use-most-recent.summary'), exactlyOne: ['use-most-recent', 'job-id'], }), wait: Flags.duration({ char: 'w', summary: messages.getMessage('flags.wait.summary'), description: messages.getMessage('flags.wait.description'), min: 0, unit: 'minutes', helpValue: '<minutes>', defaultValue: 0, }), }; async run() { const { flags } = await this.parse(OrgResumeScratch); const cache = await ScratchOrgCache.create(); const lifecycle = Lifecycle.getInstance(); const jobId = flags['use-most-recent'] ? cache.getLatestKey() : flags['job-id']; if (!jobId && flags['use-most-recent']) throw messages.createError('error.NoRecentJobId'); // oclif doesn't know that the exactlyOne flag will ensure that one of these is set, and there we definitely have a jobID. assert(jobId); const cached = cache.get(jobId); const hubBaseUrl = cached?.hubBaseUrl; const mso = new MultiStageOutput({ stages: scratchOrgLifecycleStages.map((stage) => capitalCase(stage)), title: 'Resuming Scratch Org', data: { alias: cached?.alias }, jsonEnabled: this.jsonEnabled(), postStagesBlock: [ { label: 'Request Id', type: 'dynamic-key-value', get: (data) => data?.scratchOrgInfo?.Id && terminalLink(data.scratchOrgInfo.Id, `${hubBaseUrl}/${data.scratchOrgInfo.Id}`), bold: true, }, { label: 'OrgId', type: 'dynamic-key-value', get: (data) => data?.scratchOrgInfo?.ScratchOrg, bold: true, color: 'cyan', }, { label: 'Username', type: 'dynamic-key-value', get: (data) => data?.scratchOrgInfo?.SignupUsername, bold: true, color: 'cyan', }, { label: 'Alias', type: 'static-key-value', get: (data) => data?.alias, }, ], }); lifecycle.on(scratchOrgLifecycleEventName, async (data) => { mso.skipTo(capitalCase(data.stage), data); if (data.stage === 'done') { mso.stop(); } return Promise.resolve(); }); try { const { username, scratchOrgInfo, authFields, warnings } = await scratchOrgResume(jobId, flags.wait); this.log(); this.logSuccess(messages.getMessage('success')); // TODO: Remove env var workaround const showSecretsEnvVarIsSet = envVars.getBoolean('SF_TEMP_SHOW_SECRETS', false); const accessTokenRedacted = secretsMessages.getMessage('redacted.accessToken'); const redactedAuthFields = authFields ? { ...authFields, accessToken: showSecretsEnvVarIsSet ? authFields.accessToken : accessTokenRedacted, refreshToken: undefined, clientSecret: undefined, } : undefined; const redactedScratchOrgInfo = scratchOrgInfo ? omit(scratchOrgInfo, ['AuthCode']) : undefined; if (this.jsonEnabled()) { if (showSecretsEnvVarIsSet) { this.warn(secretsMessages.getMessage('temp.envVarIsSet', ['sf org resume scratch --json'])); } else { this.warn(secretsMessages.getMessage('temp.envVarWorkaround', ['sf org resume scratch --json'])); } } return { username, scratchOrgInfo: redactedScratchOrgInfo, authFields: redactedAuthFields, warnings, orgId: authFields?.orgId, }; } catch (e) { mso.error(); if (cache.keys() && e instanceof Error && e.name === 'CacheMissError') { // we have something in the cache, but it didn't match what the user passed in throw messages.createError('error.jobIdMismatch', [jobId]); } else if (e instanceof SfError && e.name === 'StillInProgressError') { e.actions = messages.getMessages('StillInProgressError.actions'); throw e; } else { throw SfError.wrap(e); } } } } //# sourceMappingURL=scratch.js.map