From 17bc1c76ee8b1025104e5354fc529417751c4ac9 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Mon, 19 Jan 2026 09:51:26 +0100 Subject: [PATCH] Improve deploy errors on CI --- .../src/cli/prompts/deploy-release.test.ts | 92 +++++++++++++++++++ .../app/src/cli/prompts/deploy-release.ts | 8 +- 2 files changed, 96 insertions(+), 4 deletions(-) diff --git a/packages/app/src/cli/prompts/deploy-release.test.ts b/packages/app/src/cli/prompts/deploy-release.test.ts index 1f3331c14e..e7286c555f 100644 --- a/packages/app/src/cli/prompts/deploy-release.test.ts +++ b/packages/app/src/cli/prompts/deploy-release.test.ts @@ -548,6 +548,98 @@ describe('deployOrReleaseConfirmationPrompt', () => { }), ).rejects.toThrow('This deployment includes changes that require confirmation.') }) + + test('non-TTY with only updates should suggest --allow-updates flag in error', async () => { + // Given + const breakdownInfo = buildEmptyBreakdownInfo() + // Add only updates (no deletes) + breakdownInfo.extensionIdentifiersBreakdown.toCreate = [buildExtensionBreakdownInfo('new extension', 'uid-new')] + breakdownInfo.configExtensionIdentifiersBreakdown!.newFieldNames = ['new field'] + + vi.spyOn(metadata, 'addPublicMetadata').mockImplementation(async () => {}) + vi.spyOn(ui, 'isTTY').mockReturnValue(false) + + // When/Then + await expect( + deployOrReleaseConfirmationPrompt({ + ...breakdownInfo, + appTitle: 'app title', + release: true, + force: false, + }), + ).rejects.toMatchObject({ + message: 'This deployment includes changes that require confirmation.', + // tryMessage contains the suggestion with the command token + tryMessage: expect.arrayContaining([{command: '--allow-updates'}]), + }) + }) + + test('non-TTY with only deletes should suggest --allow-deletes flag in error', async () => { + // Given + const breakdownInfo = buildEmptyBreakdownInfo() + // Add only deletes (no updates) + breakdownInfo.extensionIdentifiersBreakdown.onlyRemote = [ + buildExtensionBreakdownInfo('remote extension', 'uid-remote'), + ] + + vi.spyOn(metadata, 'addPublicMetadata').mockImplementation(async () => {}) + vi.spyOn(ui, 'isTTY').mockReturnValue(false) + + // When/Then + await expect( + deployOrReleaseConfirmationPrompt({ + ...breakdownInfo, + appTitle: 'app title', + release: true, + force: false, + }), + ).rejects.toMatchObject({ + message: 'This deployment includes changes that require confirmation.', + // tryMessage contains the suggestion with the command token + tryMessage: expect.arrayContaining([{command: '--allow-deletes'}]), + }) + }) + + test('non-TTY with both updates and deletes should suggest both flags in error', async () => { + // Given + const breakdownInfo = buildCompleteBreakdownInfo() + vi.spyOn(metadata, 'addPublicMetadata').mockImplementation(async () => {}) + vi.spyOn(ui, 'isTTY').mockReturnValue(false) + + // When/Then + await expect( + deployOrReleaseConfirmationPrompt({ + ...breakdownInfo, + appTitle: 'app title', + release: true, + force: false, + }), + ).rejects.toMatchObject({ + message: 'This deployment includes changes that require confirmation.', + // tryMessage contains the suggestion with both command flags joined + tryMessage: expect.arrayContaining([{command: '--allow-updates --allow-deletes'}]), + }) + }) + + test('non-TTY without any changes should not throw error', async () => { + // Given + const breakdownInfo = buildEmptyBreakdownInfo() + const renderConfirmationPromptSpyOn = vi.spyOn(ui, 'renderConfirmationPrompt').mockResolvedValue(true) + vi.spyOn(metadata, 'addPublicMetadata').mockImplementation(async () => {}) + vi.spyOn(ui, 'isTTY').mockReturnValue(false) + + // When + const result = await deployOrReleaseConfirmationPrompt({ + ...breakdownInfo, + appTitle: 'app title', + release: true, + force: false, + }) + + // Then - should show the prompt normally since there are no changes requiring confirmation + expect(renderConfirmationPromptSpyOn).toHaveBeenCalled() + expect(result).toBe(true) + }) }) }) diff --git a/packages/app/src/cli/prompts/deploy-release.ts b/packages/app/src/cli/prompts/deploy-release.ts index 5bb428a510..5198b205e2 100644 --- a/packages/app/src/cli/prompts/deploy-release.ts +++ b/packages/app/src/cli/prompts/deploy-release.ts @@ -76,12 +76,12 @@ function shouldSkipConfirmationPrompt({ // If we're in non-TTY mode and there are changes that require confirmation, throw an error if (!isTTY() && (hasDeletes || hasUpdates)) { - let suggestedFlag = '--force' - if (hasUpdates && !hasDeletes) suggestedFlag = '--allow-updates' - if (hasDeletes && !hasUpdates) suggestedFlag = '--allow-deletes' + const suggestedFlags: string[] = [] + if (hasUpdates) suggestedFlags.push('--allow-updates') + if (hasDeletes) suggestedFlags.push('--allow-deletes') throw new AbortError('This deployment includes changes that require confirmation.', [ 'Run the command with', - {command: suggestedFlag}, + {command: suggestedFlags.join(' ')}, 'to deploy without confirmation.', ]) }