From 5aa9e58ac45d688bc9c620acb3bb4898667315bc Mon Sep 17 00:00:00 2001 From: netama Date: Thu, 1 Apr 2021 07:26:48 -0700 Subject: [PATCH] SALTO-1242 - Add dedicated command for re-generating the cache (#1966) --- packages/cli/src/commands/clean.ts | 30 +++++++++- packages/cli/src/formatter.ts | 4 +- packages/cli/test/commands/clean.test.ts | 75 +++++++++++++++++++++++- 3 files changed, 104 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/clean.ts b/packages/cli/src/commands/clean.ts index ba6bf823d9d..5b6184d8685 100644 --- a/packages/cli/src/commands/clean.ts +++ b/packages/cli/src/commands/clean.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import _ from 'lodash' import { EOL } from 'os' import { cleanWorkspace } from '@salto-io/core' import { WorkspaceComponents } from '@salto-io/workspace' @@ -22,22 +23,34 @@ import { formatCleanWorkspace, formatCancelCommand, header, formatStepStart, for import Prompts from '../prompts' import { CliExitCode } from '../types' import { WorkspaceCommandAction, createWorkspaceCommand } from '../command_builder' +import { validateWorkspace } from '../workspace/workspace' type CleanArgs = { force: boolean + regenerateCache: boolean } & WorkspaceComponents export const action: WorkspaceCommandAction = async ({ - input: { force, ...cleanArgs }, + input: { force, ...allCleanArgs }, output, workspace, }): Promise => { - const shouldCleanAnything = Object.values(cleanArgs).some(shouldClean => shouldClean) + const shouldCleanAnything = Object.values(allCleanArgs).some(shouldClean => shouldClean) if (!shouldCleanAnything) { outputLine(header(Prompts.EMPTY_PLAN), output) outputLine(EOL, output) return CliExitCode.UserInputError } + if (allCleanArgs.regenerateCache && allCleanArgs.cache) { + errorOutputLine('Cannot re-generate and clear the cache in the same operation', output) + outputLine(EOL, output) + return CliExitCode.UserInputError + } + const cleanArgs = { + ..._.omit(allCleanArgs, 'regenerateCache', 'cache'), + // should still clear the cache before re-generating it + cache: allCleanArgs.cache || allCleanArgs.regenerateCache, + } if (cleanArgs.staticResources && !(cleanArgs.state && cleanArgs.cache && cleanArgs.nacl)) { errorOutputLine('Cannot clear static resources without clearing the state, cache and nacls', output) outputLine(EOL, output) @@ -45,7 +58,7 @@ export const action: WorkspaceCommandAction = async ({ } outputLine(header( - formatCleanWorkspace(cleanArgs) + formatCleanWorkspace(allCleanArgs) ), output) if (!(force || await getUserBooleanInput(Prompts.SHOULD_EXECUTE_PLAN))) { outputLine(formatCancelCommand, output) @@ -56,6 +69,10 @@ export const action: WorkspaceCommandAction = async ({ try { await cleanWorkspace(workspace, cleanArgs) + if (allCleanArgs.regenerateCache) { + await validateWorkspace(workspace) + await workspace.flush() + } } catch (e) { errorOutputLine(formatStepFailed(Prompts.CLEAN_FAILED(e.toString())), output) return CliExitCode.AppError @@ -119,6 +136,13 @@ const cleanDef = createWorkspaceCommand({ type: 'boolean', default: false, }, + { + name: 'regenerateCache', + alias: 'x', + description: 'Regenerate the cache', + type: 'boolean', + default: false, + }, ], }, action, diff --git a/packages/cli/src/formatter.ts b/packages/cli/src/formatter.ts index 6fcffbb7566..efbf88edab4 100644 --- a/packages/cli/src/formatter.ts +++ b/packages/cli/src/formatter.ts @@ -635,7 +635,9 @@ export const formatInvalidElementCommand = (command: string): string => [ emptyLine(), ].join('\n') -export const formatCleanWorkspace = (cleanArgs: WorkspaceComponents): string => { +export const formatCleanWorkspace = ( + cleanArgs: WorkspaceComponents & { regenerateCache: boolean } +): string => { const componentsToClean = Object.entries(cleanArgs) .filter(([_comp, shouldClean]) => shouldClean) .map(([comp]) => _.startCase(comp).toLowerCase()) diff --git a/packages/cli/test/commands/clean.test.ts b/packages/cli/test/commands/clean.test.ts index 513ac94adf2..ffc6768c30e 100644 --- a/packages/cli/test/commands/clean.test.ts +++ b/packages/cli/test/commands/clean.test.ts @@ -54,6 +54,7 @@ describe('clean command', () => { staticResources: false, credentials: false, serviceConfig: false, + regenerateCache: false, }, workspace: mocks.mockWorkspace({}), })).toBe(CliExitCode.UserInputError) @@ -74,13 +75,32 @@ describe('clean command', () => { staticResources: true, credentials: true, serviceConfig: true, + regenerateCache: false, + }, + workspace: mocks.mockWorkspace({}), + })).toBe(CliExitCode.Success) + expect(callbacks.getUserBooleanInput).toHaveBeenCalledWith('Do you want to perform these actions?') + expect(output.stdout.content.search('Canceling...')).toBeGreaterThan(0) + }) + it('should prompt user and exit if no (regenerate-cache)', async () => { + jest.spyOn(callbacks, 'getUserBooleanInput').mockImplementationOnce(() => Promise.resolve(false)) + expect(await action({ + ...cliCommandArgs, + input: { + force: false, + nacl: true, + state: false, + cache: false, + staticResources: false, + credentials: false, + serviceConfig: false, + regenerateCache: true, }, workspace: mocks.mockWorkspace({}), })).toBe(CliExitCode.Success) expect(callbacks.getUserBooleanInput).toHaveBeenCalledWith('Do you want to perform these actions?') expect(output.stdout.content.search('Canceling...')).toBeGreaterThan(0) }) - it('should fail if trying to clean static resources without all dependent components', async () => { expect(await action({ ...cliCommandArgs, @@ -92,6 +112,7 @@ describe('clean command', () => { staticResources: true, credentials: true, serviceConfig: true, + regenerateCache: false, }, workspace: mocks.mockWorkspace({}), })).toBe(CliExitCode.UserInputError) @@ -99,6 +120,24 @@ describe('clean command', () => { expect(output.stderr.content.search('Cannot clear static resources without clearing the state, cache and nacls')).toBeGreaterThanOrEqual(0) }) + it('should fail if attempting to regenerate and clean the cache at the same time', async () => { + expect(await action({ + ...cliCommandArgs, + input: { + force: false, + nacl: false, + state: false, + cache: true, + staticResources: false, + credentials: false, + serviceConfig: false, + regenerateCache: true, + }, + workspace: mocks.mockWorkspace({}), + })).toBe(CliExitCode.UserInputError) + expect(callbacks.getUserBooleanInput).not.toHaveBeenCalled() + expect(output.stderr.content.search('Cannot re-generate and clear the cache in the same operation')).toBeGreaterThanOrEqual(0) + }) it('should prompt user and continue if yes', async () => { const workspace = mocks.mockWorkspace({}) expect(await action({ @@ -111,6 +150,7 @@ describe('clean command', () => { staticResources: true, credentials: true, serviceConfig: true, + regenerateCache: false, }, workspace, })).toBe(CliExitCode.Success) @@ -127,7 +167,38 @@ describe('clean command', () => { expect(output.stdout.content.search('Starting to clean')).toBeGreaterThan(0) expect(output.stdout.content.search('Finished cleaning')).toBeGreaterThan(0) }) + it('should prompt user and continue if yes (regenerate-cache)', async () => { + const workspace = mocks.mockWorkspace({}) + expect(await action({ + ...cliCommandArgs, + input: { + force: false, + nacl: false, + state: false, + cache: false, + staticResources: false, + credentials: false, + serviceConfig: false, + regenerateCache: true, + }, + workspace, + })).toBe(CliExitCode.Success) + expect(callbacks.getUserBooleanInput).toHaveBeenCalledWith('Do you want to perform these actions?') + expect(core.cleanWorkspace).toHaveBeenCalledWith(workspace, { + nacl: false, + state: false, + cache: true, + staticResources: false, + credentials: false, + serviceConfig: false, + }) + expect(workspace.errors).toHaveBeenCalledTimes(1) + // expecting 1 because there is no call from the mocked cleanWorkspace + expect(workspace.flush).toHaveBeenCalledTimes(1) + expect(output.stdout.content.search('Starting to clean')).toBeGreaterThan(0) + expect(output.stdout.content.search('Finished cleaning')).toBeGreaterThan(0) + }) it('should exit cleanly on error', async () => { jest.spyOn(core, 'cleanWorkspace').mockImplementationOnce( () => { throw new Error('something bad happened') } @@ -142,6 +213,7 @@ describe('clean command', () => { staticResources: true, credentials: true, serviceConfig: true, + regenerateCache: false, }, workspace: mocks.mockWorkspace({}), })).toBe(CliExitCode.AppError) @@ -167,6 +239,7 @@ describe('clean command', () => { staticResources: true, credentials: true, serviceConfig: true, + regenerateCache: false, }, workspace, })).toBe(CliExitCode.Success)